mirror of
https://github.com/dg/dibi.git
synced 2025-08-30 01:09:50 +02:00
Compare commits
34 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9d5d430d3d | ||
|
04bb5ede3d | ||
|
7d82ce2ff6 | ||
|
82150d120d | ||
|
0f045c0986 | ||
|
5646884899 | ||
|
af33a354d6 | ||
|
a0c86747dc | ||
|
d70e274244 | ||
|
e05eb01233 | ||
|
2ac618ffff | ||
|
1881fea0e5 | ||
|
cb82357cfb | ||
|
0a29fcb502 | ||
|
8270b7c1c3 | ||
|
73e16eb1a3 | ||
|
0b394a993d | ||
|
df3edee70b | ||
|
245da39a9f | ||
|
3df64fc3b3 | ||
|
95c3f72a17 | ||
|
d71caf0c75 | ||
|
3066fea2aa | ||
|
b00e556289 | ||
|
877dffd460 | ||
|
7049949b14 | ||
|
771e846a62 | ||
|
4e056c52dd | ||
|
40ad77cf5f | ||
|
d09b462eef | ||
|
4c796a0e0f | ||
|
304af5185d | ||
|
e046935137 | ||
|
1a77048225 |
31
.github/workflows/coding-style.yml
vendored
Normal file
31
.github/workflows/coding-style.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Coding Style
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
nette_cc:
|
||||
name: Nette Code Checker
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.0
|
||||
coverage: none
|
||||
|
||||
- run: composer create-project nette/code-checker temp/code-checker ^3 --no-progress
|
||||
- run: php temp/code-checker/code-checker --strict-types --no-progress --ignore "tests/*/fixtures"
|
||||
|
||||
|
||||
nette_cs:
|
||||
name: Nette Coding Standard
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.0
|
||||
coverage: none
|
||||
|
||||
- run: composer create-project nette/coding-standard temp/coding-standard ^3 --no-progress --ignore-platform-reqs
|
||||
- run: php temp/coding-standard/ecs check
|
21
.github/workflows/static-analysis.yml
vendored
Normal file
21
.github/workflows/static-analysis.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Static Analysis (only informative)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
phpstan:
|
||||
name: PHPStan
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.0
|
||||
coverage: none
|
||||
|
||||
- run: composer install --no-progress --prefer-dist
|
||||
- run: composer phpstan -- --no-progress
|
||||
continue-on-error: true # is only informative
|
121
.github/workflows/tests.yml
vendored
Normal file
121
.github/workflows/tests.yml
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
name: Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
env:
|
||||
php-extensions: mbstring, intl, mysqli, pgsql, sqlsrv-5.10.0beta2, pdo_sqlsrv-5.10.0beta2
|
||||
php-tools: "composer:v2, pecl"
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
php: ['7.2', '7.3', '7.4', '8.0', '8.1']
|
||||
|
||||
fail-fast: false
|
||||
|
||||
name: PHP ${{ matrix.php }} tests
|
||||
|
||||
services:
|
||||
mysql57:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_DATABASE: dibi_test
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
ports:
|
||||
- 3306:3306
|
||||
options: >-
|
||||
--health-cmd "mysqladmin ping -ppass"
|
||||
--health-interval 10s
|
||||
--health-start-period 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 10
|
||||
|
||||
mysql80:
|
||||
image: mysql:8.0
|
||||
ports:
|
||||
- 3307:3306
|
||||
options: >-
|
||||
--health-cmd="mysqladmin ping -ppass"
|
||||
--health-interval=10s
|
||||
--health-timeout=5s
|
||||
--health-retries=5
|
||||
-e MYSQL_ROOT_PASSWORD=root
|
||||
-e MYSQL_DATABASE=dibi_test
|
||||
--entrypoint sh mysql:8 -c "exec docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password"
|
||||
|
||||
postgres96:
|
||||
image: postgres:9.6
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: dibi_test
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
postgres13:
|
||||
image: postgres:13
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: dibi_test
|
||||
ports:
|
||||
- 5433:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
mssql:
|
||||
image: mcr.microsoft.com/mssql/server:latest
|
||||
env:
|
||||
ACCEPT_EULA: Y
|
||||
SA_PASSWORD: YourStrong!Passw0rd
|
||||
MSSQL_PID: Developer
|
||||
ports:
|
||||
- 1433:1433
|
||||
options: >-
|
||||
--name=mssql
|
||||
--health-cmd "/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'SELECT 1'"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: ${{ env.php-extensions }}
|
||||
tools: ${{ env.php-tools }}
|
||||
coverage: none
|
||||
|
||||
- name: Create databases.ini
|
||||
run: cp ./tests/databases.github.ini ./tests/databases.ini
|
||||
|
||||
- name: Create MS SQL Database
|
||||
run: docker exec -i mssql /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'CREATE DATABASE dibi_test'
|
||||
|
||||
- run: composer install --no-progress --prefer-dist
|
||||
- run: vendor/bin/tester -p phpdbg tests -s -C --coverage ./coverage.xml --coverage-src ./src
|
||||
- if: failure()
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: output
|
||||
path: tests/**/output
|
||||
|
||||
|
||||
- name: Save Code Coverage
|
||||
if: ${{ matrix.php == '8.0' }}
|
||||
env:
|
||||
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.4.3/php-coveralls.phar
|
||||
php php-coveralls.phar --verbose --config tests/.coveralls.yml
|
76
.travis.yml
76
.travis.yml
@@ -1,76 +0,0 @@
|
||||
language: php
|
||||
php:
|
||||
- 7.2
|
||||
- 7.3
|
||||
- 7.4
|
||||
- 8.0snapshot
|
||||
|
||||
services:
|
||||
- mysql
|
||||
- postgresql
|
||||
|
||||
before_install:
|
||||
# turn off XDebug
|
||||
- phpenv config-rm xdebug.ini || return 0
|
||||
|
||||
# Create databases.ini
|
||||
- cp ./tests/databases.travis.ini ./tests/databases.ini
|
||||
|
||||
# Create Postgre database
|
||||
- psql -c 'CREATE DATABASE dibi_test' -U postgres
|
||||
|
||||
install:
|
||||
- travis_retry composer install --no-progress --prefer-dist
|
||||
|
||||
script:
|
||||
- vendor/bin/tester tests -s
|
||||
|
||||
after_failure:
|
||||
# Print *.actual content
|
||||
- for i in $(find tests -name \*.actual); do echo "--- $i"; cat $i; echo; echo; done
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- name: Nette Code Checker
|
||||
install:
|
||||
- travis_retry composer create-project nette/code-checker temp/code-checker ^3 --no-progress
|
||||
script:
|
||||
- php temp/code-checker/code-checker --strict-types
|
||||
|
||||
|
||||
- name: Nette Coding Standard
|
||||
php: 7.4
|
||||
install:
|
||||
- travis_retry composer create-project nette/coding-standard temp/coding-standard ^3 --no-progress
|
||||
script:
|
||||
- php temp/coding-standard/ecs check src tests
|
||||
|
||||
|
||||
- stage: Static Analysis (informative)
|
||||
php: 7.4
|
||||
script:
|
||||
- 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:
|
||||
- wget https://github.com/satooshi/php-coveralls/releases/download/v1.0.1/coveralls.phar
|
||||
- php coveralls.phar --verbose --config tests/.coveralls.yml
|
||||
|
||||
|
||||
allow_failures:
|
||||
- stage: Static Analysis (informative)
|
||||
- stage: Code Coverage
|
||||
|
||||
|
||||
dist: xenial
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache
|
||||
|
||||
notifications:
|
||||
email: false
|
10
appveyor.yml
10
appveyor.yml
@@ -15,17 +15,17 @@ init:
|
||||
- SET ANSICON=121x90 (121x90)
|
||||
|
||||
install:
|
||||
# Install PHP 7.1
|
||||
# Install PHP 7.2
|
||||
- IF EXIST c:\php7 (SET PHP=0) ELSE (SET PHP=1)
|
||||
- IF %PHP%==1 mkdir c:\php7
|
||||
- IF %PHP%==1 cd c:\php7
|
||||
- IF %PHP%==1 curl https://windows.php.net/downloads/releases/archives/php-7.1.5-Win32-VC14-x64.zip --output php.zip
|
||||
- IF %PHP%==1 curl https://windows.php.net/downloads/releases/archives/php-7.2.18-Win32-VC15-x64.zip --output php.zip
|
||||
- IF %PHP%==1 7z x php.zip >nul
|
||||
- IF %PHP%==1 echo extension_dir=ext >> php.ini
|
||||
- IF %PHP%==1 echo extension=php_openssl.dll >> php.ini
|
||||
- IF %PHP%==1 appveyor DownloadFile https://files.nette.org/misc/php-sqlsrv.zip
|
||||
- IF %PHP%==1 7z x php-sqlsrv.zip >nul
|
||||
- IF %PHP%==1 copy SQLSRV\php_sqlsrv_71_ts.dll ext\php_sqlsrv_71_ts.dll
|
||||
- IF %PHP%==1 curl https://github.com/microsoft/msphpsql/releases/download/v5.8.0/Windows-7.2.zip -L --output sqlsrv.zip
|
||||
- IF %PHP%==1 7z x sqlsrv.zip >nul
|
||||
- IF %PHP%==1 copy Windows-7.2\x64\php_sqlsrv_72_ts.dll ext\php_sqlsrv_ts.dll
|
||||
- IF %PHP%==1 del /Q *.zip
|
||||
|
||||
# Install Microsoft Access Database Engine x64
|
||||
|
21
ecs.php
21
ecs.php
@@ -1,21 +0,0 @@
|
||||
<?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'],
|
||||
]);
|
||||
};
|
12
readme.md
12
readme.md
@@ -2,7 +2,7 @@
|
||||
=========================================================
|
||||
|
||||
[](https://packagist.org/packages/dibi/dibi)
|
||||
[](https://travis-ci.org/dg/dibi)
|
||||
[](https://github.com/dg/dibi/actions)
|
||||
[](https://ci.appveyor.com/project/dg/dibi/branch/master)
|
||||
[](https://github.com/dg/dibi/releases)
|
||||
[](https://github.com/dg/dibi/blob/master/license.md)
|
||||
@@ -15,12 +15,14 @@ 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.
|
||||
|
||||
|
||||
Support Project
|
||||
---------------
|
||||
Support Me
|
||||
----------
|
||||
|
||||
Do you like Dibi? Are you looking forward to the new features?
|
||||
|
||||
[](https://nette.org/make-donation?to=dibi)
|
||||
[](https://github.com/sponsors/dg)
|
||||
|
||||
Thank you!
|
||||
|
||||
|
||||
Installation
|
||||
@@ -32,7 +34,7 @@ Install Dibi via Composer:
|
||||
composer require dibi/dibi
|
||||
```
|
||||
|
||||
The Dibi 4.2 requires PHP version 7.2 and supports PHP up to 8.0.
|
||||
The Dibi 4.2 requires PHP version 7.2 and supports PHP up to 8.1.
|
||||
|
||||
|
||||
Usage
|
||||
|
@@ -22,10 +22,14 @@ class DibiExtension22 extends Nette\DI\CompilerExtension
|
||||
/** @var bool|null */
|
||||
private $debugMode;
|
||||
|
||||
/** @var bool|null */
|
||||
private $cliMode;
|
||||
|
||||
public function __construct(bool $debugMode = null)
|
||||
|
||||
public function __construct(?bool $debugMode = null, ?bool $cliMode = null)
|
||||
{
|
||||
$this->debugMode = $debugMode;
|
||||
$this->cliMode = $cliMode;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +42,11 @@ class DibiExtension22 extends Nette\DI\CompilerExtension
|
||||
$this->debugMode = $container->parameters['debugMode'];
|
||||
}
|
||||
|
||||
$useProfiler = $config['profiler'] ?? (class_exists(Tracy\Debugger::class) && $this->debugMode);
|
||||
if ($this->cliMode === null) {
|
||||
$this->cliMode = $container->parameters['consoleMode'];
|
||||
}
|
||||
|
||||
$useProfiler = $config['profiler'] ?? (class_exists(Tracy\Debugger::class) && $this->debugMode && !$this->cliMode);
|
||||
|
||||
unset($config['profiler']);
|
||||
|
||||
@@ -47,6 +55,7 @@ class DibiExtension22 extends Nette\DI\CompilerExtension
|
||||
foreach ((array) $config['flags'] as $flag) {
|
||||
$flags |= constant($flag);
|
||||
}
|
||||
|
||||
$config['flags'] = $flags;
|
||||
}
|
||||
|
||||
@@ -60,6 +69,7 @@ class DibiExtension22 extends Nette\DI\CompilerExtension
|
||||
[[Dibi\Bridges\Tracy\Panel::class, 'renderException']]
|
||||
);
|
||||
}
|
||||
|
||||
if ($useProfiler) {
|
||||
$panel = $container->addDefinition($this->prefix('panel'))
|
||||
->setFactory(Dibi\Bridges\Tracy\Panel::class, [
|
||||
|
@@ -35,7 +35,7 @@ class Panel implements Tracy\IBarPanel
|
||||
private $events = [];
|
||||
|
||||
|
||||
public function __construct($explain = true, int $filter = null)
|
||||
public function __construct($explain = true, ?int $filter = null)
|
||||
{
|
||||
$this->filter = $filter ?: Event::QUERY;
|
||||
$this->explain = $explain;
|
||||
@@ -58,6 +58,7 @@ class Panel implements Tracy\IBarPanel
|
||||
if (($event->type & $this->filter) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->events[] = $event;
|
||||
}
|
||||
|
||||
@@ -88,9 +89,10 @@ class Panel implements Tracy\IBarPanel
|
||||
foreach ($this->events as $event) {
|
||||
$totalTime += $event->time;
|
||||
}
|
||||
|
||||
return '<span title="dibi"><svg viewBox="0 0 2048 2048" style="vertical-align: bottom; width:1.23em; height:1.55em"><path fill="' . ($count ? '#b079d6' : '#aaa') . '" d="M1024 896q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0 768q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-384q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-1152q208 0 385 34.5t280 93.5 103 128v128q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-128q0-69 103-128t280-93.5 385-34.5z"/></svg><span class="tracy-label">'
|
||||
. $count . ' queries'
|
||||
. ($totalTime ? ' / ' . number_format($totalTime * 1000, 1, '.', ' ') . ' ms' : '')
|
||||
. $count . "\u{a0}queries"
|
||||
. ($totalTime ? ' / ' . number_format($totalTime * 1000, 1, '.', "\u{202f}") . "\u{202f}ms" : '')
|
||||
. '</span></span>';
|
||||
}
|
||||
|
||||
@@ -128,10 +130,11 @@ class Panel implements Tracy\IBarPanel
|
||||
$explain = @Helpers::dump($connection->nativeQuery("$cmd $event->sql"), true);
|
||||
} catch (Dibi\Exception $e) {
|
||||
}
|
||||
|
||||
[$connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime] = $backup;
|
||||
}
|
||||
|
||||
$s .= '<tr><td data-order="' . $event->time . '">' . number_format($event->time * 1000, 3, '.', ' ');
|
||||
$s .= '<tr><td data-order="' . $event->time . '">' . number_format($event->time * 1000, 3, '.', "\u{202f}");
|
||||
if ($explain) {
|
||||
static $counter;
|
||||
$counter++;
|
||||
@@ -142,6 +145,7 @@ class Panel implements Tracy\IBarPanel
|
||||
if ($explain) {
|
||||
$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');
|
||||
}
|
||||
@@ -155,8 +159,8 @@ class Panel implements Tracy\IBarPanel
|
||||
return '<style> #tracy-debug td.tracy-DibiProfiler-sql { background: white !important }
|
||||
#tracy-debug .tracy-DibiProfiler-source { color: #999 !important }
|
||||
#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') . ', '
|
||||
<h1>Queries:\u{a0}' . count($this->events)
|
||||
. ($totalTime === null ? '' : ", time:\u{a0}" . number_format($totalTime * 1000, 1, '.', "\u{202f}") . "\u{202f}ms") . ', '
|
||||
. htmlspecialchars($this->getConnectionName($singleConnection)) . '</h1>
|
||||
<div class="tracy-inner tracy-DibiProfiler">
|
||||
<table class="tracy-sortable">
|
||||
@@ -172,6 +176,6 @@ class Panel implements Tracy\IBarPanel
|
||||
$driver = $connection->getConfig('driver');
|
||||
return (is_object($driver) ? get_class($driver) : $driver)
|
||||
. ($connection->getConfig('name') ? '/' . $connection->getConfig('name') : '')
|
||||
. ($connection->getConfig('host') ? ' @ ' . $connection->getConfig('host') : '');
|
||||
. ($connection->getConfig('host') ? "\u{202f}@\u{202f}" . $connection->getConfig('host') : '');
|
||||
}
|
||||
}
|
||||
|
@@ -40,6 +40,8 @@ class Connection implements IConnection
|
||||
/** @var HashMap Substitutes for identifiers */
|
||||
private $substitutes;
|
||||
|
||||
private $transactionDepth = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Connection options: (see driver-specific options too)
|
||||
@@ -66,7 +68,7 @@ class Connection implements IConnection
|
||||
* - onConnect (array) => list of SQL queries to execute (by Connection::query()) after connection is established
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct(array $config, string $name = null)
|
||||
public function __construct(array $config, ?string $name = null)
|
||||
{
|
||||
Helpers::alias($config, 'username', 'user');
|
||||
Helpers::alias($config, 'password', 'pass');
|
||||
@@ -148,16 +150,17 @@ class Connection implements IConnection
|
||||
if ($event) {
|
||||
$this->onEvent($event->done());
|
||||
}
|
||||
|
||||
if (isset($this->config['onConnect'])) {
|
||||
foreach ($this->config['onConnect'] as $sql) {
|
||||
$this->query($sql);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (DriverException $e) {
|
||||
if ($event) {
|
||||
$this->onEvent($event->done($e));
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
@@ -189,7 +192,7 @@ class Connection implements IConnection
|
||||
* @see self::__construct
|
||||
* @return mixed
|
||||
*/
|
||||
final public function getConfig(string $key = null, $default = null)
|
||||
final public function getConfig(?string $key = null, $default = null)
|
||||
{
|
||||
return $key === null
|
||||
? $this->config
|
||||
@@ -205,6 +208,7 @@ class Connection implements IConnection
|
||||
if (!$this->driver) {
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
return $this->driver;
|
||||
}
|
||||
|
||||
@@ -230,6 +234,7 @@ class Connection implements IConnection
|
||||
if (!$this->driver) {
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
return (clone $this->translator)->translate($args);
|
||||
}
|
||||
|
||||
@@ -250,6 +255,7 @@ class Connection implements IConnection
|
||||
} else {
|
||||
echo get_class($e) . ': ' . $e->getMessage() . (PHP_SAPI === 'cli' ? "\n" : '<br>');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -285,6 +291,7 @@ class Connection implements IConnection
|
||||
if ($event) {
|
||||
$this->onEvent($event->done($e));
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -292,6 +299,7 @@ class Connection implements IConnection
|
||||
if ($event) {
|
||||
$this->onEvent($event->done($res));
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -305,10 +313,12 @@ class Connection implements IConnection
|
||||
if (!$this->driver) {
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
$rows = $this->driver->getAffectedRows();
|
||||
if ($rows === null || $rows < 0) {
|
||||
throw new Exception('Cannot retrieve number of affected rows.');
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
@@ -317,15 +327,17 @@ class Connection implements IConnection
|
||||
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getInsertId(string $sequence = null): int
|
||||
public function getInsertId(?string $sequence = null): int
|
||||
{
|
||||
if (!$this->driver) {
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
$id = $this->driver->getInsertId($sequence);
|
||||
if ($id === null) {
|
||||
throw new Exception('Cannot retrieve last generated ID.');
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
@@ -333,22 +345,27 @@ class Connection implements IConnection
|
||||
/**
|
||||
* Begins a transaction (if supported).
|
||||
*/
|
||||
public function begin(string $savepoint = null): void
|
||||
public function begin(?string $savepoint = null): void
|
||||
{
|
||||
if ($this->transactionDepth !== 0) {
|
||||
throw new \LogicException(__METHOD__ . '() call is forbidden inside a transaction() callback');
|
||||
}
|
||||
|
||||
if (!$this->driver) {
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
$event = $this->onEvent ? new Event($this, Event::BEGIN, $savepoint) : null;
|
||||
try {
|
||||
$this->driver->begin($savepoint);
|
||||
if ($event) {
|
||||
$this->onEvent($event->done());
|
||||
}
|
||||
|
||||
} catch (DriverException $e) {
|
||||
if ($event) {
|
||||
$this->onEvent($event->done($e));
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
@@ -357,22 +374,27 @@ class Connection implements IConnection
|
||||
/**
|
||||
* Commits statements in a transaction.
|
||||
*/
|
||||
public function commit(string $savepoint = null): void
|
||||
public function commit(?string $savepoint = null): void
|
||||
{
|
||||
if ($this->transactionDepth !== 0) {
|
||||
throw new \LogicException(__METHOD__ . '() call is forbidden inside a transaction() callback');
|
||||
}
|
||||
|
||||
if (!$this->driver) {
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
$event = $this->onEvent ? new Event($this, Event::COMMIT, $savepoint) : null;
|
||||
try {
|
||||
$this->driver->commit($savepoint);
|
||||
if ($event) {
|
||||
$this->onEvent($event->done());
|
||||
}
|
||||
|
||||
} catch (DriverException $e) {
|
||||
if ($event) {
|
||||
$this->onEvent($event->done($e));
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
@@ -381,22 +403,27 @@ class Connection implements IConnection
|
||||
/**
|
||||
* Rollback changes in a transaction.
|
||||
*/
|
||||
public function rollback(string $savepoint = null): void
|
||||
public function rollback(?string $savepoint = null): void
|
||||
{
|
||||
if ($this->transactionDepth !== 0) {
|
||||
throw new \LogicException(__METHOD__ . '() call is forbidden inside a transaction() callback');
|
||||
}
|
||||
|
||||
if (!$this->driver) {
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
$event = $this->onEvent ? new Event($this, Event::ROLLBACK, $savepoint) : null;
|
||||
try {
|
||||
$this->driver->rollback($savepoint);
|
||||
if ($event) {
|
||||
$this->onEvent($event->done());
|
||||
}
|
||||
|
||||
} catch (DriverException $e) {
|
||||
if ($event) {
|
||||
$this->onEvent($event->done($e));
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
@@ -407,14 +434,27 @@ class Connection implements IConnection
|
||||
*/
|
||||
public function transaction(callable $callback)
|
||||
{
|
||||
$this->begin();
|
||||
if ($this->transactionDepth === 0) {
|
||||
$this->begin();
|
||||
}
|
||||
|
||||
$this->transactionDepth++;
|
||||
try {
|
||||
$res = $callback();
|
||||
$res = $callback($this);
|
||||
} catch (\Throwable $e) {
|
||||
$this->rollback();
|
||||
$this->transactionDepth--;
|
||||
if ($this->transactionDepth === 0) {
|
||||
$this->rollback();
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
$this->commit();
|
||||
|
||||
$this->transactionDepth--;
|
||||
if ($this->transactionDepth === 0) {
|
||||
$this->commit();
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -458,6 +498,7 @@ class Connection implements IConnection
|
||||
if ($args instanceof Traversable) {
|
||||
$args = iterator_to_array($args);
|
||||
}
|
||||
|
||||
return $this->command()->insert()
|
||||
->into('%n', $table, '(%n)', array_keys($args))->values('%l', $args);
|
||||
}
|
||||
@@ -561,7 +602,7 @@ class Connection implements IConnection
|
||||
* @param callable $onProgress function (int $count, ?float $percent): void
|
||||
* @return int count of sql commands
|
||||
*/
|
||||
public function loadFile(string $file, callable $onProgress = null): int
|
||||
public function loadFile(string $file, ?callable $onProgress = null): int
|
||||
{
|
||||
return Helpers::loadFromFile($this, $file, $onProgress);
|
||||
}
|
||||
@@ -575,6 +616,7 @@ class Connection implements IConnection
|
||||
if (!$this->driver) {
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
return new Reflection\Database($this->driver->getReflector(), $this->config['database'] ?? null);
|
||||
}
|
||||
|
||||
|
@@ -65,13 +65,14 @@ class DataSource implements IDataSource
|
||||
* @param string|array $col column name or array of column names
|
||||
* @param string $as column alias
|
||||
*/
|
||||
public function select($col, string $as = null): self
|
||||
public function select($col, ?string $as = null): self
|
||||
{
|
||||
if (is_array($col)) {
|
||||
$this->cols = $col;
|
||||
} else {
|
||||
$this->cols[$col] = $as;
|
||||
}
|
||||
|
||||
$this->result = null;
|
||||
return $this;
|
||||
}
|
||||
@@ -101,6 +102,7 @@ class DataSource implements IDataSource
|
||||
} else {
|
||||
$this->sorting[$row] = $direction;
|
||||
}
|
||||
|
||||
$this->result = null;
|
||||
return $this;
|
||||
}
|
||||
@@ -109,7 +111,7 @@ class DataSource implements IDataSource
|
||||
/**
|
||||
* Limits number of rows.
|
||||
*/
|
||||
public function applyLimit(int $limit, int $offset = null): self
|
||||
public function applyLimit(int $limit, ?int $offset = null): self
|
||||
{
|
||||
$this->limit = $limit;
|
||||
$this->offset = $offset;
|
||||
@@ -135,6 +137,7 @@ class DataSource implements IDataSource
|
||||
if ($this->result === null) {
|
||||
$this->result = $this->connection->nativeQuery($this->__toString());
|
||||
}
|
||||
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
@@ -185,7 +188,7 @@ class DataSource implements IDataSource
|
||||
/**
|
||||
* Fetches all records from table like $key => $value pairs.
|
||||
*/
|
||||
public function fetchPairs(string $key = null, string $value = null): array
|
||||
public function fetchPairs(?string $key = null, ?string $value = null): array
|
||||
{
|
||||
return $this->getResult()->fetchPairs($key, $value);
|
||||
}
|
||||
@@ -262,6 +265,7 @@ class DataSource implements IDataSource
|
||||
)->fetchSingle())
|
||||
: $this->getTotalCount();
|
||||
}
|
||||
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
@@ -276,6 +280,7 @@ class DataSource implements IDataSource
|
||||
'SELECT COUNT(*) FROM ' . $this->sql
|
||||
)->fetchSingle());
|
||||
}
|
||||
|
||||
return $this->totalCount;
|
||||
}
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ class DateTime extends \DateTimeImmutable
|
||||
/**
|
||||
* @param string|int $time
|
||||
*/
|
||||
public function __construct($time = 'now', \DateTimeZone $timezone = null)
|
||||
public function __construct($time = 'now', ?\DateTimeZone $timezone = null)
|
||||
{
|
||||
$timezone = $timezone ?: new \DateTimeZone(date_default_timezone_get());
|
||||
if (is_numeric($time)) {
|
||||
|
@@ -42,17 +42,17 @@ class DummyDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
|
||||
}
|
||||
|
||||
|
||||
public function begin(string $savepoint = null): void
|
||||
public function begin(?string $savepoint = null): void
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public function commit(string $savepoint = null): void
|
||||
public function commit(?string $savepoint = null): void
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public function rollback(string $savepoint = null): void
|
||||
public function rollback(?string $savepoint = null): void
|
||||
{
|
||||
}
|
||||
|
||||
|
@@ -101,10 +101,10 @@ class FirebirdDriver implements Dibi\Driver
|
||||
} else {
|
||||
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode(), $sql);
|
||||
}
|
||||
|
||||
} elseif (is_resource($res)) {
|
||||
return $this->createResultDriver($res);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -131,11 +131,12 @@ class FirebirdDriver implements Dibi\Driver
|
||||
* Begins a transaction (if supported).
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function begin(string $savepoint = null): void
|
||||
public function begin(?string $savepoint = null): void
|
||||
{
|
||||
if ($savepoint !== null) {
|
||||
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
|
||||
}
|
||||
|
||||
$this->transaction = ibase_trans($this->getResource());
|
||||
$this->inTransaction = true;
|
||||
}
|
||||
@@ -145,7 +146,7 @@ class FirebirdDriver implements Dibi\Driver
|
||||
* Commits statements in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function commit(string $savepoint = null): void
|
||||
public function commit(?string $savepoint = null): void
|
||||
{
|
||||
if ($savepoint !== null) {
|
||||
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
|
||||
@@ -163,7 +164,7 @@ class FirebirdDriver implements Dibi\Driver
|
||||
* Rollback changes in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function rollback(string $savepoint = null): void
|
||||
public function rollback(?string $savepoint = null): void
|
||||
{
|
||||
if ($savepoint !== null) {
|
||||
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
|
||||
|
@@ -47,6 +47,7 @@ class FirebirdReflector implements Dibi\Reflector
|
||||
'view' => $row[1] === 'TRUE',
|
||||
];
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
@@ -99,6 +100,7 @@ class FirebirdReflector implements Dibi\Reflector
|
||||
'autoincrement' => false,
|
||||
];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
@@ -131,6 +133,7 @@ class FirebirdReflector implements Dibi\Reflector
|
||||
$indexes[$key]['table'] = $table;
|
||||
$indexes[$key]['columns'][$row['FIELD_POSITION']] = $row['FIELD_NAME'];
|
||||
}
|
||||
|
||||
return $indexes;
|
||||
}
|
||||
|
||||
@@ -159,6 +162,7 @@ class FirebirdReflector implements Dibi\Reflector
|
||||
'table' => $table,
|
||||
];
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
@@ -179,6 +183,7 @@ class FirebirdReflector implements Dibi\Reflector
|
||||
while ($row = $res->fetch(false)) {
|
||||
$indices[] = $row[0];
|
||||
}
|
||||
|
||||
return $indices;
|
||||
}
|
||||
|
||||
@@ -201,6 +206,7 @@ class FirebirdReflector implements Dibi\Reflector
|
||||
while ($row = $res->fetch(false)) {
|
||||
$constraints[] = $row[0];
|
||||
}
|
||||
|
||||
return $constraints;
|
||||
}
|
||||
|
||||
@@ -209,7 +215,7 @@ class FirebirdReflector implements Dibi\Reflector
|
||||
* Returns metadata for all triggers in a table or database.
|
||||
* (Only if user has permissions on ALTER TABLE, INSERT/UPDATE/DELETE record in table)
|
||||
*/
|
||||
public function getTriggersMeta(string $table = null): array
|
||||
public function getTriggersMeta(?string $table = null): array
|
||||
{
|
||||
$res = $this->driver->query(
|
||||
"
|
||||
@@ -248,6 +254,7 @@ class FirebirdReflector implements Dibi\Reflector
|
||||
'enabled' => trim($row['TRIGGER_ENABLED']) === 'TRUE',
|
||||
];
|
||||
}
|
||||
|
||||
return $triggers;
|
||||
}
|
||||
|
||||
@@ -256,7 +263,7 @@ class FirebirdReflector implements Dibi\Reflector
|
||||
* Returns list of triggers for given table.
|
||||
* (Only if user has permissions on ALTER TABLE, INSERT/UPDATE/DELETE record in table)
|
||||
*/
|
||||
public function getTriggers(string $table = null): array
|
||||
public function getTriggers(?string $table = null): array
|
||||
{
|
||||
$q = 'SELECT TRIM(RDB$TRIGGER_NAME)
|
||||
FROM RDB$TRIGGERS
|
||||
@@ -270,6 +277,7 @@ class FirebirdReflector implements Dibi\Reflector
|
||||
while ($row = $res->fetch(false)) {
|
||||
$triggers[] = $row[0];
|
||||
}
|
||||
|
||||
return $triggers;
|
||||
}
|
||||
|
||||
@@ -321,6 +329,7 @@ class FirebirdReflector implements Dibi\Reflector
|
||||
$procedures[$key]['params'][$io][$num]['type'] = trim($row['FIELD_TYPE']);
|
||||
$procedures[$key]['params'][$io][$num]['size'] = $row['FIELD_LENGTH'];
|
||||
}
|
||||
|
||||
return $procedures;
|
||||
}
|
||||
|
||||
@@ -338,6 +347,7 @@ class FirebirdReflector implements Dibi\Reflector
|
||||
while ($row = $res->fetch(false)) {
|
||||
$procedures[] = $row[0];
|
||||
}
|
||||
|
||||
return $procedures;
|
||||
}
|
||||
|
||||
@@ -356,6 +366,7 @@ class FirebirdReflector implements Dibi\Reflector
|
||||
while ($row = $res->fetch(false)) {
|
||||
$generators[] = $row[0];
|
||||
}
|
||||
|
||||
return $generators;
|
||||
}
|
||||
|
||||
@@ -374,6 +385,7 @@ class FirebirdReflector implements Dibi\Reflector
|
||||
while ($row = $res->fetch(false)) {
|
||||
$functions[] = $row[0];
|
||||
}
|
||||
|
||||
return $functions;
|
||||
}
|
||||
}
|
||||
|
@@ -126,6 +126,7 @@ class FirebirdResult implements Dibi\ResultDriver
|
||||
'nativetype' => $row['type'],
|
||||
];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
@@ -43,6 +43,7 @@ class MySqlReflector implements Dibi\Reflector
|
||||
'view' => isset($row[1]) && $row[1] === 'VIEW',
|
||||
];
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
@@ -67,6 +68,7 @@ class MySqlReflector implements Dibi\Reflector
|
||||
'vendor' => $row,
|
||||
];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
@@ -84,6 +86,7 @@ class MySqlReflector implements Dibi\Reflector
|
||||
$indexes[$row['Key_name']]['primary'] = $row['Key_name'] === 'PRIMARY';
|
||||
$indexes[$row['Key_name']]['columns'][$row['Seq_in_index'] - 1] = $row['Column_name'];
|
||||
}
|
||||
|
||||
return array_values($indexes);
|
||||
}
|
||||
|
||||
@@ -123,6 +126,7 @@ class MySqlReflector implements Dibi\Reflector
|
||||
$foreignKeys[$keyName]['onDelete'] = $row['DELETE_RULE'];
|
||||
$foreignKeys[$keyName]['onUpdate'] = $row['UPDATE_RULE'];
|
||||
}
|
||||
|
||||
return array_values($foreignKeys);
|
||||
}
|
||||
}
|
||||
|
@@ -72,7 +72,7 @@ class MySqliDriver implements Dibi\Driver
|
||||
$host = ini_get('mysqli.default_host');
|
||||
if ($host) {
|
||||
$config['host'] = $host;
|
||||
$config['port'] = ini_get('mysqli.default_port');
|
||||
$config['port'] = (int) ini_get('mysqli.default_port');
|
||||
} else {
|
||||
$config['host'] = null;
|
||||
$config['port'] = null;
|
||||
@@ -88,6 +88,7 @@ class MySqliDriver implements Dibi\Driver
|
||||
$this->connection->options($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
@$this->connection->real_connect( // intentionally @
|
||||
(empty($config['persistent']) ? '' : 'p:') . $config['host'],
|
||||
$config['username'],
|
||||
@@ -153,6 +154,7 @@ class MySqliDriver implements Dibi\Driver
|
||||
} elseif ($res instanceof \mysqli_result) {
|
||||
return $this->createResultDriver($res);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -191,6 +193,7 @@ class MySqliDriver implements Dibi\Driver
|
||||
foreach ($matches as $m) {
|
||||
$res[$m[1]] = (int) $m[2];
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -219,7 +222,7 @@ class MySqliDriver implements Dibi\Driver
|
||||
* Begins a transaction (if supported).
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function begin(string $savepoint = null): void
|
||||
public function begin(?string $savepoint = null): void
|
||||
{
|
||||
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION');
|
||||
}
|
||||
@@ -229,7 +232,7 @@ class MySqliDriver implements Dibi\Driver
|
||||
* Commits statements in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function commit(string $savepoint = null): void
|
||||
public function commit(?string $savepoint = null): void
|
||||
{
|
||||
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
|
||||
}
|
||||
@@ -239,7 +242,7 @@ class MySqliDriver implements Dibi\Driver
|
||||
* Rollback changes in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function rollback(string $savepoint = null): void
|
||||
public function rollback(?string $savepoint = null): void
|
||||
{
|
||||
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
|
||||
}
|
||||
@@ -250,7 +253,11 @@ class MySqliDriver implements Dibi\Driver
|
||||
*/
|
||||
public function getResource(): ?\mysqli
|
||||
{
|
||||
return @$this->connection->thread_id ? $this->connection : null;
|
||||
try {
|
||||
return @$this->connection->thread_id ? $this->connection : null;
|
||||
} catch (\Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -319,6 +326,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'");
|
||||
}
|
||||
|
||||
|
@@ -55,6 +55,7 @@ class MySqliResult implements Dibi\ResultDriver
|
||||
if (!$this->buffered) {
|
||||
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
|
||||
}
|
||||
|
||||
return $this->resultSet->num_rows;
|
||||
}
|
||||
|
||||
@@ -80,6 +81,7 @@ class MySqliResult implements Dibi\ResultDriver
|
||||
if (!$this->buffered) {
|
||||
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
|
||||
}
|
||||
|
||||
return $this->resultSet->data_seek($row);
|
||||
}
|
||||
|
||||
@@ -107,6 +109,7 @@ class MySqliResult implements Dibi\ResultDriver
|
||||
$types[$value] = substr($key, 12);
|
||||
}
|
||||
}
|
||||
|
||||
$types[MYSQLI_TYPE_TINY] = $types[MYSQLI_TYPE_SHORT] = $types[MYSQLI_TYPE_LONG] = 'INT';
|
||||
}
|
||||
|
||||
@@ -123,6 +126,7 @@ class MySqliResult implements Dibi\ResultDriver
|
||||
'vendor' => $row,
|
||||
];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
@@ -96,6 +96,7 @@ class OdbcDriver implements Dibi\Driver
|
||||
? $this->createResultDriver($res)
|
||||
: null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -122,7 +123,7 @@ class OdbcDriver implements Dibi\Driver
|
||||
* Begins a transaction (if supported).
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function begin(string $savepoint = null): void
|
||||
public function begin(?string $savepoint = null): void
|
||||
{
|
||||
if (!odbc_autocommit($this->connection, PHP_VERSION_ID < 80000 ? 0 : false)) {
|
||||
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
|
||||
@@ -134,11 +135,12 @@ class OdbcDriver implements Dibi\Driver
|
||||
* Commits statements in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function commit(string $savepoint = null): void
|
||||
public function commit(?string $savepoint = null): void
|
||||
{
|
||||
if (!odbc_commit($this->connection)) {
|
||||
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
|
||||
}
|
||||
|
||||
odbc_autocommit($this->connection, PHP_VERSION_ID < 80000 ? 1 : true);
|
||||
}
|
||||
|
||||
@@ -147,11 +149,12 @@ class OdbcDriver implements Dibi\Driver
|
||||
* Rollback changes in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function rollback(string $savepoint = null): void
|
||||
public function rollback(?string $savepoint = null): void
|
||||
{
|
||||
if (!odbc_rollback($this->connection)) {
|
||||
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
|
||||
}
|
||||
|
||||
odbc_autocommit($this->connection, PHP_VERSION_ID < 80000 ? 1 : true);
|
||||
}
|
||||
|
||||
|
@@ -44,6 +44,7 @@ class OdbcReflector implements Dibi\Reflector
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
odbc_free_result($res);
|
||||
return $tables;
|
||||
}
|
||||
@@ -68,6 +69,7 @@ class OdbcReflector implements Dibi\Reflector
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
odbc_free_result($res);
|
||||
return $columns;
|
||||
}
|
||||
|
@@ -72,11 +72,13 @@ class OdbcResult implements Dibi\ResultDriver
|
||||
if (!odbc_fetch_row($set, ++$this->row)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$count = odbc_num_fields($set);
|
||||
$cols = [];
|
||||
for ($i = 1; $i <= $count; $i++) {
|
||||
$cols[] = odbc_result($set, $i);
|
||||
}
|
||||
|
||||
return $cols;
|
||||
}
|
||||
}
|
||||
@@ -116,6 +118,7 @@ class OdbcResult implements Dibi\ResultDriver
|
||||
'nativetype' => odbc_field_type($this->resultSet, $i),
|
||||
];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
@@ -104,6 +104,7 @@ class OracleDriver implements Dibi\Driver
|
||||
$err = oci_error($this->connection);
|
||||
throw new Dibi\DriverException($err['message'], $err['code'], $sql);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -147,7 +148,7 @@ class OracleDriver implements Dibi\Driver
|
||||
/**
|
||||
* Begins a transaction (if supported).
|
||||
*/
|
||||
public function begin(string $savepoint = null): void
|
||||
public function begin(?string $savepoint = null): void
|
||||
{
|
||||
$this->autocommit = false;
|
||||
}
|
||||
@@ -157,12 +158,13 @@ class OracleDriver implements Dibi\Driver
|
||||
* Commits statements in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function commit(string $savepoint = null): void
|
||||
public function commit(?string $savepoint = null): void
|
||||
{
|
||||
if (!oci_commit($this->connection)) {
|
||||
$err = oci_error($this->connection);
|
||||
throw new Dibi\DriverException($err['message'], $err['code']);
|
||||
}
|
||||
|
||||
$this->autocommit = true;
|
||||
}
|
||||
|
||||
@@ -171,12 +173,13 @@ class OracleDriver implements Dibi\Driver
|
||||
* Rollback changes in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function rollback(string $savepoint = null): void
|
||||
public function rollback(?string $savepoint = null): void
|
||||
{
|
||||
if (!oci_rollback($this->connection)) {
|
||||
$err = oci_error($this->connection);
|
||||
throw new Dibi\DriverException($err['message'], $err['code']);
|
||||
}
|
||||
|
||||
$this->autocommit = true;
|
||||
}
|
||||
|
||||
|
@@ -44,6 +44,7 @@ class OracleReflector implements Dibi\Reflector
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
@@ -66,6 +67,7 @@ class OracleReflector implements Dibi\Reflector
|
||||
'vendor' => $row,
|
||||
];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
@@ -100,6 +100,7 @@ class OracleResult implements Dibi\ResultDriver
|
||||
'nativetype' => $type === 'NUMBER' && oci_field_scale($this->resultSet, $i) === 0 ? 'INTEGER' : $type,
|
||||
];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
@@ -60,7 +60,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.');
|
||||
}
|
||||
|
||||
} else {
|
||||
try {
|
||||
$this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
|
||||
@@ -69,6 +68,7 @@ class PdoDriver implements Dibi\Driver
|
||||
if ($e->getMessage() === 'could not find driver') {
|
||||
throw new Dibi\NotSupportedException('PHP extension for PDO is not loaded.');
|
||||
}
|
||||
|
||||
throw new Dibi\DriverException($e->getMessage(), $e->getCode());
|
||||
}
|
||||
}
|
||||
@@ -144,7 +144,7 @@ class PdoDriver implements Dibi\Driver
|
||||
* Begins a transaction (if supported).
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function begin(string $savepoint = null): void
|
||||
public function begin(?string $savepoint = null): void
|
||||
{
|
||||
if (!$this->connection->beginTransaction()) {
|
||||
$err = $this->connection->errorInfo();
|
||||
@@ -157,7 +157,7 @@ class PdoDriver implements Dibi\Driver
|
||||
* Commits statements in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function commit(string $savepoint = null): void
|
||||
public function commit(?string $savepoint = null): void
|
||||
{
|
||||
if (!$this->connection->commit()) {
|
||||
$err = $this->connection->errorInfo();
|
||||
@@ -170,7 +170,7 @@ class PdoDriver implements Dibi\Driver
|
||||
* Rollback changes in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function rollback(string $savepoint = null): void
|
||||
public function rollback(?string $savepoint = null): void
|
||||
{
|
||||
if (!$this->connection->rollBack()) {
|
||||
$err = $this->connection->errorInfo();
|
||||
@@ -366,15 +366,18 @@ class PdoDriver implements Dibi\Driver
|
||||
$sql .= ' LIMIT ' . ($limit ?? '18446744073709551615')
|
||||
. ($offset ? ' OFFSET ' . $offset : '');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'pgsql':
|
||||
if ($limit !== null) {
|
||||
$sql .= ' LIMIT ' . $limit;
|
||||
}
|
||||
|
||||
if ($offset) {
|
||||
$sql .= ' OFFSET ' . $offset;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'sqlite':
|
||||
@@ -382,6 +385,7 @@ class PdoDriver implements Dibi\Driver
|
||||
$sql .= ' LIMIT ' . ($limit ?? '-1')
|
||||
. ($offset ? ' OFFSET ' . $offset : '');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'oci':
|
||||
@@ -394,6 +398,7 @@ class PdoDriver implements Dibi\Driver
|
||||
} elseif ($limit !== null) {
|
||||
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . $limit;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'mssql':
|
||||
@@ -406,10 +411,10 @@ class PdoDriver implements Dibi\Driver
|
||||
} elseif ($offset) {
|
||||
$sql = sprintf('%s OFFSET %d ROWS', rtrim($sql), $offset);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
// break omitted
|
||||
|
||||
case 'odbc':
|
||||
if ($offset) {
|
||||
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
|
||||
@@ -419,7 +424,6 @@ class PdoDriver implements Dibi\Driver
|
||||
break;
|
||||
}
|
||||
// break omitted
|
||||
|
||||
default:
|
||||
throw new Dibi\NotSupportedException('PDO or driver does not support applying limit or offset.');
|
||||
}
|
||||
|
@@ -85,6 +85,7 @@ class PdoResult implements Dibi\ResultDriver
|
||||
if ($row === false) {
|
||||
throw new Dibi\NotSupportedException('Driver does not support meta data.');
|
||||
}
|
||||
|
||||
$row += [
|
||||
'table' => null,
|
||||
'native_type' => 'VAR_STRING',
|
||||
@@ -99,6 +100,7 @@ class PdoResult implements Dibi\ResultDriver
|
||||
'vendor' => $row,
|
||||
];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
@@ -11,6 +11,7 @@ namespace Dibi\Drivers;
|
||||
|
||||
use Dibi;
|
||||
use Dibi\Helpers;
|
||||
use PgSql;
|
||||
|
||||
|
||||
/**
|
||||
@@ -29,7 +30,7 @@ class PostgreDriver implements Dibi\Driver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** @var resource */
|
||||
/** @var resource|PgSql\Connection */
|
||||
private $connection;
|
||||
|
||||
/** @var int|null Affected rows */
|
||||
@@ -63,6 +64,7 @@ class PostgreDriver implements Dibi\Driver
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$connectType = $config['connect_type'] ?? PGSQL_CONNECT_FORCE_NEW;
|
||||
|
||||
set_error_handler(function (int $severity, string $message) use (&$error) {
|
||||
@@ -74,7 +76,7 @@ class PostgreDriver implements Dibi\Driver
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
if (!is_resource($this->connection)) {
|
||||
if (!is_resource($this->connection) && !$this->connection instanceof PgSql\Connection) {
|
||||
throw new Dibi\DriverException($error ?: 'Connecting error.');
|
||||
}
|
||||
|
||||
@@ -120,17 +122,18 @@ class PostgreDriver implements Dibi\Driver
|
||||
if ($res === false) {
|
||||
throw static::createException(pg_last_error($this->connection), null, $sql);
|
||||
|
||||
} elseif (is_resource($res)) {
|
||||
} elseif (is_resource($res) || $res instanceof PgSql\Result) {
|
||||
$this->affectedRows = Helpers::false2Null(pg_affected_rows($res));
|
||||
if (pg_num_fields($res)) {
|
||||
return $this->createResultDriver($res);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static function createException(string $message, $code = null, string $sql = null): Dibi\DriverException
|
||||
public static function createException(string $message, $code = null, ?string $sql = null): Dibi\DriverException
|
||||
{
|
||||
if ($code === null && preg_match('#^ERROR:\s+(\S+):\s*#', $message, $m)) {
|
||||
$code = $m[1];
|
||||
@@ -186,9 +189,9 @@ class PostgreDriver implements Dibi\Driver
|
||||
* Begins a transaction (if supported).
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function begin(string $savepoint = null): void
|
||||
public function begin(?string $savepoint = null): void
|
||||
{
|
||||
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION');
|
||||
$this->query($savepoint ? "SAVEPOINT {$this->escapeIdentifier($savepoint)}" : 'START TRANSACTION');
|
||||
}
|
||||
|
||||
|
||||
@@ -196,9 +199,9 @@ class PostgreDriver implements Dibi\Driver
|
||||
* Commits statements in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function commit(string $savepoint = null): void
|
||||
public function commit(?string $savepoint = null): void
|
||||
{
|
||||
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
|
||||
$this->query($savepoint ? "RELEASE SAVEPOINT {$this->escapeIdentifier($savepoint)}" : 'COMMIT');
|
||||
}
|
||||
|
||||
|
||||
@@ -206,9 +209,9 @@ class PostgreDriver implements Dibi\Driver
|
||||
* Rollback changes in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function rollback(string $savepoint = null): void
|
||||
public function rollback(?string $savepoint = null): void
|
||||
{
|
||||
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
|
||||
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT {$this->escapeIdentifier($savepoint)}" : 'ROLLBACK');
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +230,9 @@ class PostgreDriver implements Dibi\Driver
|
||||
*/
|
||||
public function getResource()
|
||||
{
|
||||
return is_resource($this->connection) ? $this->connection : null;
|
||||
return is_resource($this->connection) || $this->connection instanceof PgSql\Connection
|
||||
? $this->connection
|
||||
: null;
|
||||
}
|
||||
|
||||
|
||||
@@ -258,18 +263,20 @@ class PostgreDriver implements Dibi\Driver
|
||||
*/
|
||||
public function escapeText(string $value): string
|
||||
{
|
||||
if (!is_resource($this->connection)) {
|
||||
if (!$this->getResource()) {
|
||||
throw new Dibi\Exception('Lost connection to server.');
|
||||
}
|
||||
|
||||
return "'" . pg_escape_string($this->connection, $value) . "'";
|
||||
}
|
||||
|
||||
|
||||
public function escapeBinary(string $value): string
|
||||
{
|
||||
if (!is_resource($this->connection)) {
|
||||
if (!$this->getResource()) {
|
||||
throw new Dibi\Exception('Lost connection to server.');
|
||||
}
|
||||
|
||||
return "'" . pg_escape_bytea($this->connection, $value) . "'";
|
||||
}
|
||||
|
||||
@@ -325,9 +332,11 @@ class PostgreDriver implements Dibi\Driver
|
||||
if ($limit < 0 || $offset < 0) {
|
||||
throw new Dibi\NotSupportedException('Negative offset or limit.');
|
||||
}
|
||||
|
||||
if ($limit !== null) {
|
||||
$sql .= ' LIMIT ' . $limit;
|
||||
}
|
||||
|
||||
if ($offset) {
|
||||
$sql .= ' OFFSET ' . $offset;
|
||||
}
|
||||
|
@@ -28,9 +28,6 @@ class PostgreReflector implements Dibi\Reflector
|
||||
|
||||
public function __construct(Dibi\Driver $driver, string $version)
|
||||
{
|
||||
if ($version < 7.4) {
|
||||
throw new Dibi\DriverException('Reflection requires PostgreSQL 7.4 and newer.');
|
||||
}
|
||||
$this->driver = $driver;
|
||||
$this->version = $version;
|
||||
}
|
||||
@@ -69,6 +66,7 @@ class PostgreReflector implements Dibi\Reflector
|
||||
while ($row = $res->fetch(true)) {
|
||||
$tables[] = $row;
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
@@ -105,7 +103,7 @@ class PostgreReflector implements Dibi\Reflector
|
||||
a.atttypmod-4 AS character_maximum_length,
|
||||
NOT a.attnotnull AS is_nullable,
|
||||
a.attnum AS ordinal_position,
|
||||
adef.adsrc AS column_default
|
||||
pg_get_expr(adef.adbin, adef.adrelid) AS column_default
|
||||
FROM
|
||||
pg_attribute a
|
||||
JOIN pg_type ON a.atttypid = pg_type.oid
|
||||
@@ -134,6 +132,7 @@ class PostgreReflector implements Dibi\Reflector
|
||||
'vendor' => $row,
|
||||
];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
@@ -183,6 +182,7 @@ class PostgreReflector implements Dibi\Reflector
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($indexes);
|
||||
}
|
||||
|
||||
|
@@ -11,6 +11,7 @@ namespace Dibi\Drivers;
|
||||
|
||||
use Dibi;
|
||||
use Dibi\Helpers;
|
||||
use PgSql;
|
||||
|
||||
|
||||
/**
|
||||
@@ -20,7 +21,7 @@ class PostgreResult implements Dibi\ResultDriver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** @var resource */
|
||||
/** @var resource|PgSql\Result */
|
||||
private $resultSet;
|
||||
|
||||
/** @var bool */
|
||||
@@ -28,7 +29,7 @@ class PostgreResult implements Dibi\ResultDriver
|
||||
|
||||
|
||||
/**
|
||||
* @param resource $resultSet
|
||||
* @param resource|PgSql\Result $resultSet
|
||||
*/
|
||||
public function __construct($resultSet)
|
||||
{
|
||||
@@ -102,18 +103,21 @@ class PostgreResult implements Dibi\ResultDriver
|
||||
: $row['name'];
|
||||
$columns[] = $row;
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the result set resource.
|
||||
* @return resource|null
|
||||
* @return resource|PgSql\Result|null
|
||||
*/
|
||||
public function getResultResource()
|
||||
{
|
||||
$this->autoFree = false;
|
||||
return is_resource($this->resultSet) ? $this->resultSet : null;
|
||||
return is_resource($this->resultSet) || $this->resultSet instanceof PgSql\Result
|
||||
? $this->resultSet
|
||||
: null;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -57,7 +57,7 @@ class SqliteDriver implements Dibi\Driver
|
||||
} else {
|
||||
try {
|
||||
$this->connection = new SQLite3($config['database']);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
throw new Dibi\DriverException($e->getMessage(), $e->getCode());
|
||||
}
|
||||
}
|
||||
@@ -92,6 +92,7 @@ class SqliteDriver implements Dibi\Driver
|
||||
} elseif ($res instanceof \SQLite3Result && $res->numColumns()) {
|
||||
return $this->createResultDriver($res);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -145,7 +146,7 @@ class SqliteDriver implements Dibi\Driver
|
||||
* Begins a transaction (if supported).
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function begin(string $savepoint = null): void
|
||||
public function begin(?string $savepoint = null): void
|
||||
{
|
||||
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'BEGIN');
|
||||
}
|
||||
@@ -155,7 +156,7 @@ class SqliteDriver implements Dibi\Driver
|
||||
* Commits statements in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function commit(string $savepoint = null): void
|
||||
public function commit(?string $savepoint = null): void
|
||||
{
|
||||
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
|
||||
}
|
||||
@@ -165,7 +166,7 @@ class SqliteDriver implements Dibi\Driver
|
||||
* Rollback changes in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function rollback(string $savepoint = null): void
|
||||
public function rollback(?string $savepoint = null): void
|
||||
{
|
||||
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
|
||||
}
|
||||
|
@@ -44,6 +44,7 @@ class SqliteReflector implements Dibi\Reflector
|
||||
while ($row = $res->fetch(true)) {
|
||||
$tables[] = $row;
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
@@ -70,6 +71,7 @@ class SqliteReflector implements Dibi\Reflector
|
||||
'vendor' => $row,
|
||||
];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
@@ -103,8 +105,10 @@ class SqliteReflector implements Dibi\Reflector
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$indexes[$index]['primary'] = (bool) $primary;
|
||||
}
|
||||
|
||||
if (!$indexes) { // @see http://www.sqlite.org/lang_createtable.html#rowid
|
||||
foreach ($columns as $column) {
|
||||
if ($column['vendor']['pk']) {
|
||||
@@ -142,6 +146,7 @@ class SqliteReflector implements Dibi\Reflector
|
||||
$keys[$row['id']]['foreign'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($keys);
|
||||
}
|
||||
}
|
||||
|
@@ -99,6 +99,7 @@ class SqliteResult implements Dibi\ResultDriver
|
||||
'nativetype' => $types[$this->resultSet->columnType($i)] ?? null, // buggy in PHP 7.4.4 & 7.3.16, bug 79414
|
||||
];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
@@ -53,7 +53,9 @@ class SqlsrvDriver implements Dibi\Driver
|
||||
|
||||
if (isset($config['resource'])) {
|
||||
$this->connection = $config['resource'];
|
||||
|
||||
if (!is_resource($this->connection)) {
|
||||
throw new \InvalidArgumentException("Configuration option 'resource' is not resource.");
|
||||
}
|
||||
} else {
|
||||
$options = $config['options'];
|
||||
|
||||
@@ -63,13 +65,16 @@ class SqlsrvDriver implements Dibi\Driver
|
||||
$options['UID'] = (string) $options['UID'];
|
||||
$options['Database'] = (string) $options['Database'];
|
||||
|
||||
sqlsrv_configure('WarningsReturnAsErrors', 0);
|
||||
$this->connection = sqlsrv_connect($config['host'], $options);
|
||||
if (!is_resource($this->connection)) {
|
||||
$info = sqlsrv_errors(SQLSRV_ERR_ERRORS);
|
||||
throw new Dibi\DriverException($info[0]['message'], $info[0]['code']);
|
||||
}
|
||||
|
||||
sqlsrv_configure('WarningsReturnAsErrors', 1);
|
||||
}
|
||||
|
||||
if (!is_resource($this->connection)) {
|
||||
$info = sqlsrv_errors();
|
||||
throw new Dibi\DriverException($info[0]['message'], $info[0]['code']);
|
||||
}
|
||||
$this->version = sqlsrv_server_info($this->connection)['SQLServerVersion'];
|
||||
}
|
||||
|
||||
@@ -102,6 +107,7 @@ class SqlsrvDriver implements Dibi\Driver
|
||||
? $this->createResultDriver($res)
|
||||
: null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -125,6 +131,7 @@ class SqlsrvDriver implements Dibi\Driver
|
||||
$row = sqlsrv_fetch_array($res, SQLSRV_FETCH_NUMERIC);
|
||||
return Dibi\Helpers::intVal($row[0]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -133,7 +140,7 @@ class SqlsrvDriver implements Dibi\Driver
|
||||
* Begins a transaction (if supported).
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function begin(string $savepoint = null): void
|
||||
public function begin(?string $savepoint = null): void
|
||||
{
|
||||
sqlsrv_begin_transaction($this->connection);
|
||||
}
|
||||
@@ -143,7 +150,7 @@ class SqlsrvDriver implements Dibi\Driver
|
||||
* Commits statements in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function commit(string $savepoint = null): void
|
||||
public function commit(?string $savepoint = null): void
|
||||
{
|
||||
sqlsrv_commit($this->connection);
|
||||
}
|
||||
@@ -153,7 +160,7 @@ class SqlsrvDriver implements Dibi\Driver
|
||||
* Rollback changes in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function rollback(string $savepoint = null): void
|
||||
public function rollback(?string $savepoint = null): void
|
||||
{
|
||||
sqlsrv_rollback($this->connection);
|
||||
}
|
||||
@@ -262,7 +269,6 @@ class SqlsrvDriver implements Dibi\Driver
|
||||
} elseif ($limit !== null) {
|
||||
$sql = sprintf('SELECT TOP (%d) * FROM (%s) t', $limit, $sql);
|
||||
}
|
||||
|
||||
} elseif ($limit !== null) {
|
||||
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
|
||||
$sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit);
|
||||
|
@@ -42,6 +42,7 @@ class SqlsrvReflector implements Dibi\Reflector
|
||||
'view' => isset($row[1]) && $row[1] === 'VIEW',
|
||||
];
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
@@ -91,6 +92,7 @@ class SqlsrvReflector implements Dibi\Reflector
|
||||
'vendor' => $row,
|
||||
];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
@@ -114,6 +116,7 @@ class SqlsrvReflector implements Dibi\Reflector
|
||||
$indexes[$row['name']]['primary'] = $row['is_primary_key'] === 1;
|
||||
$indexes[$row['name']]['columns'] = $keyUsages[$row['name']] ?? [];
|
||||
}
|
||||
|
||||
return array_values($indexes);
|
||||
}
|
||||
|
||||
|
@@ -96,6 +96,7 @@ class SqlsrvResult implements Dibi\ResultDriver
|
||||
'nativetype' => $fieldMetadata['Type'],
|
||||
];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
@@ -53,7 +53,7 @@ class Event
|
||||
public $source;
|
||||
|
||||
|
||||
public function __construct(Connection $connection, int $type, string $sql = null)
|
||||
public function __construct(Connection $connection, int $type, ?string $sql = null)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->type = $type;
|
||||
|
@@ -136,6 +136,7 @@ class Fluent implements IDataSource
|
||||
if (isset(self::$masks[$clause])) {
|
||||
$this->clauses = array_fill_keys(self::$masks[$clause], null);
|
||||
}
|
||||
|
||||
$this->cursor = &$this->clauses[$clause];
|
||||
$this->cursor = [];
|
||||
$this->command = $clause;
|
||||
@@ -165,7 +166,6 @@ class Fluent implements IDataSource
|
||||
$this->cursor[] = $sep;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// append to currect flow
|
||||
if ($args === [self::REMOVE]) {
|
||||
@@ -203,6 +203,7 @@ class Fluent implements IDataSource
|
||||
if ($arg instanceof self) {
|
||||
$arg = new Literal("($arg)");
|
||||
}
|
||||
|
||||
$this->cursor[] = $arg;
|
||||
}
|
||||
|
||||
@@ -245,6 +246,7 @@ class Fluent implements IDataSource
|
||||
} else {
|
||||
unset($this->flags[$flag]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -291,7 +293,7 @@ class Fluent implements IDataSource
|
||||
* @return Result|int|null result set or number of affected rows
|
||||
* @throws Exception
|
||||
*/
|
||||
public function execute(string $return = null)
|
||||
public function execute(?string $return = null)
|
||||
{
|
||||
$res = $this->query($this->_export());
|
||||
switch ($return) {
|
||||
@@ -332,7 +334,7 @@ class Fluent implements IDataSource
|
||||
/**
|
||||
* Fetches all records from table.
|
||||
*/
|
||||
public function fetchAll(int $offset = null, int $limit = null): array
|
||||
public function fetchAll(?int $offset = null, ?int $limit = null): array
|
||||
{
|
||||
return $this->query($this->_export(null, ['%ofs %lmt', $offset, $limit]))->fetchAll();
|
||||
}
|
||||
@@ -351,7 +353,7 @@ class Fluent implements IDataSource
|
||||
/**
|
||||
* Fetches all records from table like $key => $value pairs.
|
||||
*/
|
||||
public function fetchPairs(string $key = null, string $value = null): array
|
||||
public function fetchPairs(?string $key = null, ?string $value = null): array
|
||||
{
|
||||
return $this->query($this->_export())->fetchPairs($key, $value);
|
||||
}
|
||||
@@ -360,7 +362,7 @@ class Fluent implements IDataSource
|
||||
/**
|
||||
* Required by the IteratorAggregate interface.
|
||||
*/
|
||||
public function getIterator(int $offset = null, int $limit = null): ResultIterator
|
||||
public function getIterator(?int $offset = null, ?int $limit = null): ResultIterator
|
||||
{
|
||||
return $this->query($this->_export(null, ['%ofs %lmt', $offset, $limit]))->getIterator();
|
||||
}
|
||||
@@ -369,7 +371,7 @@ class Fluent implements IDataSource
|
||||
/**
|
||||
* Generates and prints SQL query or it's part.
|
||||
*/
|
||||
public function test(string $clause = null): bool
|
||||
public function test(?string $clause = null): bool
|
||||
{
|
||||
return $this->connection->test($this->_export($clause));
|
||||
}
|
||||
@@ -390,6 +392,7 @@ class Fluent implements IDataSource
|
||||
$method = array_shift($setup);
|
||||
$res->$method(...$setup);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -420,7 +423,7 @@ class Fluent implements IDataSource
|
||||
/**
|
||||
* Generates parameters for Translator.
|
||||
*/
|
||||
protected function _export(string $clause = null, array $args = []): array
|
||||
protected function _export(?string $clause = null, array $args = []): array
|
||||
{
|
||||
if ($clause === null) {
|
||||
$data = $this->clauses;
|
||||
@@ -428,7 +431,6 @@ class Fluent implements IDataSource
|
||||
$args = array_merge(['%lmt %ofs', $data['LIMIT'][0] ?? null, $data['OFFSET'][0] ?? null], $args);
|
||||
unset($data['LIMIT'], $data['OFFSET']);
|
||||
}
|
||||
|
||||
} else {
|
||||
$clause = self::$normalizer->$clause;
|
||||
if (array_key_exists($clause, $this->clauses)) {
|
||||
@@ -444,6 +446,7 @@ class Fluent implements IDataSource
|
||||
if ($clause === $this->command && $this->flags) {
|
||||
$args[] = implode(' ', array_keys($this->flags));
|
||||
}
|
||||
|
||||
foreach ($statement as $arg) {
|
||||
$args[] = $arg;
|
||||
}
|
||||
@@ -464,6 +467,7 @@ class Fluent implements IDataSource
|
||||
$s .= 'By';
|
||||
trigger_error("Did you mean '$s'?", E_USER_NOTICE);
|
||||
}
|
||||
|
||||
return strtoupper(preg_replace('#[a-z](?=[A-Z])#', '$0 ', $s));
|
||||
}
|
||||
|
||||
@@ -475,6 +479,7 @@ class Fluent implements IDataSource
|
||||
$this->clauses[$clause] = &$val;
|
||||
unset($val);
|
||||
}
|
||||
|
||||
$this->cursor = &$foo;
|
||||
}
|
||||
}
|
||||
|
@@ -50,6 +50,7 @@ final class HashMap extends HashMapBase
|
||||
if ($nm === '') {
|
||||
$nm = "\xFF";
|
||||
}
|
||||
|
||||
$this->$nm = $val;
|
||||
}
|
||||
|
||||
@@ -58,7 +59,7 @@ final class HashMap extends HashMapBase
|
||||
{
|
||||
if ($nm === '') {
|
||||
$nm = "\xFF";
|
||||
return isset($this->$nm) ? $this->$nm : $this->$nm = $this->getCallback()('');
|
||||
return isset($this->$nm) && true ? $this->$nm : $this->$nm = $this->getCallback()('');
|
||||
} else {
|
||||
return $this->$nm = $this->getCallback()($nm);
|
||||
}
|
||||
|
@@ -41,6 +41,7 @@ class Helpers
|
||||
$spaces = $maxLen - mb_strlen($col) + 2;
|
||||
echo "$col" . str_repeat(' ', $spaces) . "$val\n";
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
@@ -53,6 +54,7 @@ class Helpers
|
||||
foreach ($row as $col => $foo) {
|
||||
echo "\t\t<th>" . htmlspecialchars((string) $col) . "</th>\n";
|
||||
}
|
||||
|
||||
echo "\t</tr>\n</thead>\n<tbody>\n";
|
||||
}
|
||||
|
||||
@@ -60,6 +62,7 @@ class Helpers
|
||||
foreach ($row as $col) {
|
||||
echo "\t\t<td>", htmlspecialchars((string) $col), "</td>\n";
|
||||
}
|
||||
|
||||
echo "\t</tr>\n";
|
||||
}
|
||||
|
||||
@@ -104,6 +107,7 @@ class Helpers
|
||||
}
|
||||
}, $sql);
|
||||
}
|
||||
|
||||
echo trim($sql) . "\n\n";
|
||||
|
||||
} else {
|
||||
@@ -150,6 +154,7 @@ class Helpers
|
||||
$best = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $best;
|
||||
}
|
||||
|
||||
@@ -181,6 +186,7 @@ class Helpers
|
||||
{
|
||||
static $patterns = [
|
||||
'^_' => Type::TEXT, // PostgreSQL arrays
|
||||
'RANGE$' => Type::TEXT, // PostgreSQL range types
|
||||
'BYTEA|BLOB|BIN' => Type::BINARY,
|
||||
'TEXT|CHAR|POINT|INTERVAL|STRING' => Type::TEXT,
|
||||
'YEAR|BYTE|COUNTER|SERIAL|INT|LONG|SHORT|^TINY$' => Type::INTEGER,
|
||||
@@ -197,6 +203,7 @@ class Helpers
|
||||
return $val;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -207,6 +214,7 @@ class Helpers
|
||||
if (self::$types === null) {
|
||||
self::$types = new HashMap([self::class, 'detectType']);
|
||||
}
|
||||
|
||||
return self::$types;
|
||||
}
|
||||
|
||||
@@ -232,7 +240,7 @@ class Helpers
|
||||
* Import SQL dump from file.
|
||||
* @return int count of sql commands
|
||||
*/
|
||||
public static function loadFromFile(Connection $connection, string $file, callable $onProgress = null): int
|
||||
public static function loadFromFile(Connection $connection, string $file, ?callable $onProgress = null): int
|
||||
{
|
||||
@set_time_limit(0); // intentionally @
|
||||
|
||||
@@ -259,7 +267,6 @@ class Helpers
|
||||
if ($onProgress) {
|
||||
$onProgress($count, isset($stat['size']) ? $size * 100 / $stat['size'] : null);
|
||||
}
|
||||
|
||||
} else {
|
||||
$sql .= $s;
|
||||
}
|
||||
@@ -272,6 +279,7 @@ class Helpers
|
||||
$onProgress($count, isset($stat['size']) ? 100 : null);
|
||||
}
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
return $count;
|
||||
}
|
||||
@@ -293,6 +301,7 @@ class Helpers
|
||||
if (is_float($value * 1)) {
|
||||
throw new Exception("Number $value is greater than integer.");
|
||||
}
|
||||
|
||||
return (int) $value;
|
||||
} else {
|
||||
throw new Exception("Expected number, '$value' given.");
|
||||
|
@@ -29,7 +29,7 @@ class FileLogger
|
||||
private $errorsOnly;
|
||||
|
||||
|
||||
public function __construct(string $file, int $filter = null, bool $errorsOnly = false)
|
||||
public function __construct(string $file, ?int $filter = null, bool $errorsOnly = false)
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->filter = $filter ?: Dibi\Event::QUERY;
|
||||
@@ -54,6 +54,7 @@ class FileLogger
|
||||
if ($code = $event->result->getCode()) {
|
||||
$message = "[$code] $message";
|
||||
}
|
||||
|
||||
$this->writeToFile(
|
||||
$event,
|
||||
"ERROR: $message"
|
||||
|
@@ -36,7 +36,7 @@ class Column
|
||||
private $info;
|
||||
|
||||
|
||||
public function __construct(Dibi\Reflector $reflector = null, array $info)
|
||||
public function __construct(?Dibi\Reflector $reflector, array $info)
|
||||
{
|
||||
$this->reflector = $reflector;
|
||||
$this->info = $info;
|
||||
@@ -66,6 +66,7 @@ class Column
|
||||
if (empty($this->info['table']) || !$this->reflector) {
|
||||
throw new Dibi\Exception('Table is unknown or not available.');
|
||||
}
|
||||
|
||||
return new Table($this->reflector, ['name' => $this->info['table']]);
|
||||
}
|
||||
|
||||
|
@@ -33,7 +33,7 @@ class Database
|
||||
private $tables;
|
||||
|
||||
|
||||
public function __construct(Dibi\Reflector $reflector, string $name = null)
|
||||
public function __construct(Dibi\Reflector $reflector, ?string $name = null)
|
||||
{
|
||||
$this->reflector = $reflector;
|
||||
$this->name = $name;
|
||||
@@ -62,6 +62,7 @@ class Database
|
||||
foreach ($this->tables as $table) {
|
||||
$res[] = $table->getName();
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
@@ -54,6 +54,7 @@ class Result
|
||||
foreach ($this->columns as $column) {
|
||||
$res[] = $fullNames ? $column->getFullName() : $column->getName();
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
@@ -85,6 +85,7 @@ class Table
|
||||
foreach ($this->columns as $column) {
|
||||
$res[] = $column->getName();
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -152,6 +153,7 @@ class Table
|
||||
foreach ($info['columns'] as $key => $name) {
|
||||
$info['columns'][$key] = $this->columns[strtolower($name)];
|
||||
}
|
||||
|
||||
$this->indexes[strtolower($info['name'])] = new Index($info);
|
||||
if (!empty($info['primary'])) {
|
||||
$this->primaryKey = $this->indexes[strtolower($info['name'])];
|
||||
|
@@ -170,6 +170,7 @@ class Result implements IDataSource
|
||||
if ($row === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->fetched = true;
|
||||
$this->normalize($row);
|
||||
if ($this->rowFactory) {
|
||||
@@ -177,6 +178,7 @@ class Result implements IDataSource
|
||||
} elseif ($this->rowClass) {
|
||||
return new $this->rowClass($row);
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
@@ -191,6 +193,7 @@ class Result implements IDataSource
|
||||
if ($row === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->fetched = true;
|
||||
$this->normalize($row);
|
||||
return reset($row);
|
||||
@@ -201,7 +204,7 @@ class Result implements IDataSource
|
||||
* Fetches all records from table.
|
||||
* @return Row[]|array[]
|
||||
*/
|
||||
final public function fetchAll(int $offset = null, int $limit = null): array
|
||||
final public function fetchAll(?int $offset = null, ?int $limit = null): array
|
||||
{
|
||||
$limit = $limit ?? -1;
|
||||
$this->seek($offset ?: 0);
|
||||
@@ -215,6 +218,7 @@ class Result implements IDataSource
|
||||
if ($limit === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
$limit--;
|
||||
$data[] = $row;
|
||||
} while ($row = $this->fetch());
|
||||
@@ -287,7 +291,6 @@ class Result implements IDataSource
|
||||
} else {
|
||||
$x = &$x->{$assoc[$i + 1]};
|
||||
}
|
||||
|
||||
} elseif ($as !== '|') { // associative-array node
|
||||
$x = &$x[(string) $row->$as];
|
||||
}
|
||||
@@ -345,7 +348,6 @@ class Result implements IDataSource
|
||||
} else {
|
||||
$x = &$x[$assoc[$i + 1]];
|
||||
}
|
||||
|
||||
} elseif ($as === '@') { // "object" node
|
||||
if ($x === null) {
|
||||
$x = clone $row;
|
||||
@@ -354,7 +356,6 @@ class Result implements IDataSource
|
||||
} else {
|
||||
$x = &$x->{$assoc[$i + 1]};
|
||||
}
|
||||
|
||||
} else { // associative-array node
|
||||
$x = &$x[(string) $row->$as];
|
||||
}
|
||||
@@ -376,7 +377,7 @@ class Result implements IDataSource
|
||||
* Fetches all records from table like $key => $value pairs.
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
final public function fetchPairs(string $key = null, string $value = null): array
|
||||
final public function fetchPairs(?string $key = null, ?string $value = null): array
|
||||
{
|
||||
$this->seek(0);
|
||||
$row = $this->fetch();
|
||||
@@ -398,6 +399,7 @@ class Result implements IDataSource
|
||||
do {
|
||||
$data[] = $row[$key];
|
||||
} while ($row = $this->fetch());
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
@@ -412,6 +414,7 @@ class Result implements IDataSource
|
||||
do {
|
||||
$data[] = $row[$value];
|
||||
} while ($row = $this->fetch());
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
@@ -455,6 +458,7 @@ class Result implements IDataSource
|
||||
if (!isset($row[$key])) { // null
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = $row[$key];
|
||||
$format = $this->formats[$type] ?? null;
|
||||
|
||||
@@ -478,9 +482,11 @@ class Result implements IDataSource
|
||||
} elseif ($p !== false && $e !== false) {
|
||||
$value = rtrim($value, '.');
|
||||
}
|
||||
|
||||
if ($value === '' || $value[0] === '.') {
|
||||
$value = '0' . $value;
|
||||
}
|
||||
|
||||
$row[$key] = $value === str_replace(',', '.', (string) ($float = (float) $value))
|
||||
? $float
|
||||
: $value;
|
||||
@@ -489,13 +495,12 @@ class Result implements IDataSource
|
||||
$row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
|
||||
|
||||
} elseif ($type === Type::DATETIME || $type === Type::DATE || $type === Type::TIME) {
|
||||
if ($value && substr((string) $value, 0, 3) !== '000') { // '', null, false, '0000-00-00', ...
|
||||
if ($value && substr((string) $value, 0, 7) !== '0000-00') { // '', null, false, '0000-00-00', ...
|
||||
$value = new DateTime($value);
|
||||
$row[$key] = $format ? $value->format($format) : $value;
|
||||
} else {
|
||||
$row[$key] = null;
|
||||
}
|
||||
|
||||
} elseif ($type === Type::TIME_INTERVAL) {
|
||||
preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)\z#', $value, $m);
|
||||
$value = new \DateInterval("PT$m[2]H$m[3]M$m[4]S");
|
||||
@@ -513,7 +518,6 @@ class Result implements IDataSource
|
||||
} else {
|
||||
$row[$key] = json_decode($value, $format === 'array');
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new \RuntimeException('Unexpected type ' . $type);
|
||||
}
|
||||
@@ -590,6 +594,7 @@ class Result implements IDataSource
|
||||
if ($this->meta === null) {
|
||||
$this->meta = new Reflection\Result($this->getResultDriver());
|
||||
}
|
||||
|
||||
return $this->meta;
|
||||
}
|
||||
|
||||
|
@@ -44,6 +44,7 @@ class ResultIterator implements \Iterator, \Countable
|
||||
}
|
||||
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
/**
|
||||
* Returns the key of the current element.
|
||||
* @return mixed
|
||||
@@ -54,6 +55,7 @@ class ResultIterator implements \Iterator, \Countable
|
||||
}
|
||||
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
/**
|
||||
* Returns the current element.
|
||||
* @return mixed
|
||||
|
@@ -33,15 +33,17 @@ class Row implements \ArrayAccess, \IteratorAggregate, \Countable
|
||||
* Converts value to DateTime object.
|
||||
* @return DateTime|string|null
|
||||
*/
|
||||
public function asDateTime(string $key, string $format = null)
|
||||
public function asDateTime(string $key, ?string $format = null)
|
||||
{
|
||||
$time = $this[$key];
|
||||
if (!$time instanceof DateTime) {
|
||||
if (!$time || substr((string) $time, 0, 3) === '000') { // '', null, false, '0000-00-00', ...
|
||||
if (!$time || substr((string) $time, 0, 7) === '0000-00') { // '', null, false, '0000-00-00', ...
|
||||
return null;
|
||||
}
|
||||
|
||||
$time = new DateTime($time);
|
||||
}
|
||||
|
||||
return $format === null ? $time : $time->format($format);
|
||||
}
|
||||
|
||||
@@ -53,40 +55,47 @@ class Row implements \ArrayAccess, \IteratorAggregate, \Countable
|
||||
}
|
||||
|
||||
|
||||
public function __isset(string $key): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/********************* interfaces ArrayAccess, Countable & IteratorAggregate ****************d*g**/
|
||||
|
||||
|
||||
final public function count()
|
||||
final public function count(): int
|
||||
{
|
||||
return count((array) $this);
|
||||
}
|
||||
|
||||
|
||||
final public function getIterator()
|
||||
final public function getIterator(): \ArrayIterator
|
||||
{
|
||||
return new \ArrayIterator($this);
|
||||
}
|
||||
|
||||
|
||||
final public function offsetSet($nm, $val)
|
||||
final public function offsetSet($nm, $val): void
|
||||
{
|
||||
$this->$nm = $val;
|
||||
}
|
||||
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
final public function offsetGet($nm)
|
||||
{
|
||||
return $this->$nm;
|
||||
}
|
||||
|
||||
|
||||
final public function offsetExists($nm)
|
||||
final public function offsetExists($nm): bool
|
||||
{
|
||||
return isset($this->$nm);
|
||||
}
|
||||
|
||||
|
||||
final public function offsetUnset($nm)
|
||||
final public function offsetUnset($nm): void
|
||||
{
|
||||
unset($this->$nm);
|
||||
}
|
||||
|
@@ -46,7 +46,7 @@ trait Strict
|
||||
public static function __callStatic(string $name, array $args)
|
||||
{
|
||||
$rc = new ReflectionClass(static::class);
|
||||
$items = array_intersect($rc->getMethods(ReflectionMethod::IS_PUBLIC), $rc->getMethods(ReflectionMethod::IS_STATIC));
|
||||
$items = array_filter($rc->getMethods(\ReflectionMethod::IS_STATIC), function ($m) { return $m->isPublic(); });
|
||||
$items = array_map(function ($item) { return $item->getName(); }, $items);
|
||||
$hint = ($t = Helpers::getSuggestion($items, $name))
|
||||
? ", did you mean $t()?"
|
||||
@@ -67,8 +67,9 @@ trait Strict
|
||||
$ret = $this->$m();
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$rc = new ReflectionClass($this);
|
||||
$items = array_diff($rc->getProperties(ReflectionProperty::IS_PUBLIC), $rc->getProperties(ReflectionProperty::IS_STATIC));
|
||||
$items = array_filter($rc->getProperties(ReflectionProperty::IS_PUBLIC), function ($p) { return !$p->isStatic(); });
|
||||
$items = array_map(function ($item) { return $item->getName(); }, $items);
|
||||
$hint = ($t = Helpers::getSuggestion($items, $name))
|
||||
? ", did you mean $$t?"
|
||||
@@ -84,7 +85,7 @@ trait Strict
|
||||
public function __set(string $name, $value)
|
||||
{
|
||||
$rc = new ReflectionClass($this);
|
||||
$items = array_diff($rc->getProperties(ReflectionProperty::IS_PUBLIC), $rc->getProperties(ReflectionProperty::IS_STATIC));
|
||||
$items = array_filter($rc->getProperties(ReflectionProperty::IS_PUBLIC), function ($p) { return !$p->isStatic(); });
|
||||
$items = array_map(function ($item) { return $item->getName(); }, $items);
|
||||
$hint = ($t = Helpers::getSuggestion($items, $name))
|
||||
? ", did you mean $$t?"
|
||||
|
@@ -69,6 +69,7 @@ final class Translator
|
||||
while (count($args) === 1 && is_array($args[0])) { // implicit array expansion
|
||||
$args = array_values($args[0]);
|
||||
}
|
||||
|
||||
$this->args = $args;
|
||||
$this->errors = [];
|
||||
|
||||
@@ -116,6 +117,7 @@ XX
|
||||
throw new PcreException;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -138,8 +140,10 @@ XX
|
||||
if ($lastArr === $cursor - 1) {
|
||||
$sql[] = ',';
|
||||
}
|
||||
|
||||
$sql[] = $this->formatValue($arg, $commandIns ? 'l' : 'a');
|
||||
}
|
||||
|
||||
$lastArr = $cursor;
|
||||
continue;
|
||||
}
|
||||
@@ -148,7 +152,6 @@ XX
|
||||
$sql[] = $this->formatValue($arg, null);
|
||||
} // while
|
||||
|
||||
|
||||
if ($comment) {
|
||||
$sql[] = '*/';
|
||||
}
|
||||
@@ -214,13 +217,14 @@ XX
|
||||
} else {
|
||||
$op = '= ';
|
||||
}
|
||||
|
||||
$vx[] = $k . $op . $v;
|
||||
}
|
||||
|
||||
} else {
|
||||
$vx[] = $this->formatValue($v, 'ex');
|
||||
}
|
||||
}
|
||||
|
||||
return '(' . implode(') ' . strtoupper($modifier) . ' (', $vx) . ')';
|
||||
|
||||
case 'n': // key, key, ... identifier names
|
||||
@@ -232,6 +236,7 @@ XX
|
||||
$vx[] = $this->identifiers->{$pair[0]};
|
||||
}
|
||||
}
|
||||
|
||||
return implode(', ', $vx);
|
||||
|
||||
|
||||
@@ -241,6 +246,7 @@ XX
|
||||
$vx[] = $this->identifiers->{$pair[0]} . '='
|
||||
. $this->formatValue($v, $pair[1] ?? (is_array($v) ? 'ex!' : null));
|
||||
}
|
||||
|
||||
return implode(', ', $vx);
|
||||
|
||||
|
||||
@@ -250,6 +256,7 @@ XX
|
||||
$pair = explode('%', (string) $k, 2); // split into identifier & modifier
|
||||
$vx[] = $this->formatValue($v, $pair[1] ?? (is_array($v) ? 'ex!' : null));
|
||||
}
|
||||
|
||||
return '(' . (($vx || $modifier === 'l') ? implode(', ', $vx) : 'NULL') . ')';
|
||||
|
||||
|
||||
@@ -259,6 +266,7 @@ XX
|
||||
$kx[] = $this->identifiers->{$pair[0]};
|
||||
$vx[] = $this->formatValue($v, $pair[1] ?? (is_array($v) ? 'ex!' : null));
|
||||
}
|
||||
|
||||
return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')';
|
||||
|
||||
case 'm': // (key, key, ...) VALUES (val, val, ...), (val, val, ...), ...
|
||||
@@ -281,9 +289,11 @@ XX
|
||||
$vx[$k2][] = $this->formatValue($v2, $pair[1] ?? (is_array($v2) ? 'ex!' : null));
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($vx as $k => $v) {
|
||||
$vx[$k] = '(' . implode(', ', $v) . ')';
|
||||
}
|
||||
|
||||
return '(' . implode(', ', $kx) . ') VALUES ' . implode(', ', $vx);
|
||||
|
||||
case 'by': // key ASC, key DESC
|
||||
@@ -297,6 +307,7 @@ XX
|
||||
$vx[] = $this->identifiers->$v;
|
||||
}
|
||||
}
|
||||
|
||||
return implode(', ', $vx);
|
||||
|
||||
case 'ex!':
|
||||
@@ -310,10 +321,15 @@ XX
|
||||
foreach ($value as $v) {
|
||||
$vx[] = $this->formatValue($v, $modifier);
|
||||
}
|
||||
|
||||
return implode(', ', $vx);
|
||||
}
|
||||
}
|
||||
|
||||
// object-to-scalar procession
|
||||
if ($value instanceof \BackedEnum && is_scalar($value->value)) {
|
||||
$value = $value->value;
|
||||
}
|
||||
|
||||
// with modifier procession
|
||||
if ($modifier) {
|
||||
@@ -400,6 +416,7 @@ XX
|
||||
} elseif (!$value instanceof \DateTimeInterface) {
|
||||
$value = new DateTime($value);
|
||||
}
|
||||
|
||||
return $modifier === 'd'
|
||||
? $this->driver->escapeDate($value)
|
||||
: $this->driver->escapeDateTime($value);
|
||||
@@ -439,6 +456,7 @@ XX
|
||||
throw new PcreException;
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
||||
case 'SQL': // preserve as real SQL (TODO: rename to %sql)
|
||||
@@ -469,7 +487,6 @@ XX
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// without modifier procession
|
||||
if (is_string($value)) {
|
||||
return $this->driver->escapeText($value);
|
||||
@@ -551,6 +568,7 @@ XX
|
||||
$this->comment = true;
|
||||
return '/*';
|
||||
}
|
||||
|
||||
return '';
|
||||
|
||||
} elseif ($mod === 'else') {
|
||||
@@ -563,7 +581,6 @@ XX
|
||||
$this->comment = true;
|
||||
return '/*';
|
||||
}
|
||||
|
||||
} elseif ($mod === 'end') {
|
||||
$this->ifLevel--;
|
||||
if ($this->ifLevelStart === $this->ifLevel + 1) {
|
||||
@@ -572,6 +589,7 @@ XX
|
||||
$this->comment = false;
|
||||
return '*/';
|
||||
}
|
||||
|
||||
return '';
|
||||
|
||||
} elseif ($mod === 'ex') { // array expansion
|
||||
@@ -586,6 +604,7 @@ XX
|
||||
} else {
|
||||
$this->limit = Helpers::intVal($arg);
|
||||
}
|
||||
|
||||
return '';
|
||||
|
||||
} elseif ($mod === 'ofs') { // apply offset
|
||||
@@ -596,6 +615,7 @@ XX
|
||||
} else {
|
||||
$this->offset = Helpers::intVal($arg);
|
||||
}
|
||||
|
||||
return '';
|
||||
|
||||
} else { // default processing
|
||||
@@ -649,6 +669,7 @@ XX
|
||||
$v = $this->driver->escapeIdentifier($v);
|
||||
}
|
||||
}
|
||||
|
||||
return implode('.', $parts);
|
||||
}
|
||||
}
|
||||
|
@@ -45,7 +45,7 @@ class dibi
|
||||
|
||||
/** version */
|
||||
public const
|
||||
VERSION = '4.2.0';
|
||||
VERSION = '4.2.6';
|
||||
|
||||
/** sorting order */
|
||||
public const
|
||||
@@ -107,7 +107,7 @@ class dibi
|
||||
* Retrieve active connection.
|
||||
* @throws Dibi\Exception
|
||||
*/
|
||||
public static function getConnection(string $name = null): Dibi\Connection
|
||||
public static function getConnection(?string $name = null): Dibi\Connection
|
||||
{
|
||||
if ($name === null) {
|
||||
if (self::$connection === null) {
|
||||
@@ -163,7 +163,7 @@ class dibi
|
||||
/**
|
||||
* Strips microseconds part.
|
||||
*/
|
||||
public static function stripMicroseconds(\DateTimeInterface $dt): \DateTimeInterface
|
||||
public static function stripMicroseconds(DateTimeInterface $dt): DateTimeInterface
|
||||
{
|
||||
$class = get_class($dt);
|
||||
return new $class($dt->format('Y-m-d H:i:s'), $dt->getTimezone());
|
||||
|
@@ -22,7 +22,7 @@ class Exception extends \Exception
|
||||
/**
|
||||
* @param int|string $code
|
||||
*/
|
||||
public function __construct(string $message = '', $code = 0, string $sql = null, \Throwable $previous = null)
|
||||
public function __construct(string $message = '', $code = 0, ?string $sql = null, ?\Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, 0, $previous);
|
||||
$this->code = $code;
|
||||
@@ -93,7 +93,7 @@ class ProcedureException extends Exception
|
||||
/**
|
||||
* Construct the exception.
|
||||
*/
|
||||
public function __construct(string $message = '', int $code = 0, string $severity = '', string $sql = null)
|
||||
public function __construct(string $message = '', int $code = 0, string $severity = '', ?string $sql = null)
|
||||
{
|
||||
parent::__construct($message, $code, $sql);
|
||||
$this->severity = $severity;
|
||||
|
@@ -51,19 +51,19 @@ interface Driver
|
||||
* Begins a transaction (if supported).
|
||||
* @throws DriverException
|
||||
*/
|
||||
function begin(string $savepoint = null): void;
|
||||
function begin(?string $savepoint = null): void;
|
||||
|
||||
/**
|
||||
* Commits statements in a transaction.
|
||||
* @throws DriverException
|
||||
*/
|
||||
function commit(string $savepoint = null): void;
|
||||
function commit(?string $savepoint = null): void;
|
||||
|
||||
/**
|
||||
* Rollback changes in a transaction.
|
||||
* @throws DriverException
|
||||
*/
|
||||
function rollback(string $savepoint = null): void;
|
||||
function rollback(?string $savepoint = null): void;
|
||||
|
||||
/**
|
||||
* Returns the connection resource.
|
||||
@@ -224,20 +224,20 @@ interface IConnection
|
||||
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
|
||||
* @throws Exception
|
||||
*/
|
||||
function getInsertId(string $sequence = null): int;
|
||||
function getInsertId(?string $sequence = null): int;
|
||||
|
||||
/**
|
||||
* Begins a transaction (if supported).
|
||||
*/
|
||||
function begin(string $savepoint = null): void;
|
||||
function begin(?string $savepoint = null): void;
|
||||
|
||||
/**
|
||||
* Commits statements in a transaction.
|
||||
*/
|
||||
function commit(string $savepoint = null): void;
|
||||
function commit(?string $savepoint = null): void;
|
||||
|
||||
/**
|
||||
* Rollback changes in a transaction.
|
||||
*/
|
||||
function rollback(string $savepoint = null): void;
|
||||
function rollback(?string $savepoint = null): void;
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# for php-coveralls
|
||||
service_name: travis-ci
|
||||
service_name: github-actions
|
||||
coverage_clover: coverage.xml
|
||||
json_path: coverage.json
|
||||
|
89
tests/databases.github.ini
Normal file
89
tests/databases.github.ini
Normal file
@@ -0,0 +1,89 @@
|
||||
[sqlite] ; default
|
||||
driver = sqlite
|
||||
database = :memory:
|
||||
system = sqlite
|
||||
|
||||
[sqlite pdo]
|
||||
driver = pdo
|
||||
dsn = "sqlite::memory:"
|
||||
system = sqlite
|
||||
|
||||
[mysql 5.7]
|
||||
driver = mysqli
|
||||
host = "127.0.0.1"
|
||||
database = dibi_test
|
||||
username = root
|
||||
password = root
|
||||
port = 3306
|
||||
system = mysql
|
||||
|
||||
[mysql 5.7pdo]
|
||||
driver = pdo
|
||||
dsn = "mysql:host=127.0.0.1;port=3306;dbname=dibi_test"
|
||||
user = root
|
||||
password = root
|
||||
system = mysql
|
||||
|
||||
[mysql 8.0]
|
||||
driver = mysqli
|
||||
host = "127.0.0.1"
|
||||
database = dibi_test
|
||||
username = root
|
||||
password = root
|
||||
port = 3307
|
||||
system = mysql
|
||||
|
||||
[mysql 8.0pdo]
|
||||
driver = pdo
|
||||
dsn = "mysql:host=127.0.0.1;port=3307;dbname=dibi_test"
|
||||
user = root
|
||||
password = root
|
||||
system = mysql
|
||||
|
||||
[postgre 9.6]
|
||||
driver = postgre
|
||||
host = "127.0.0.1"
|
||||
database = dibi_test
|
||||
username = postgres
|
||||
password = postgres
|
||||
port = 5432
|
||||
system = postgre
|
||||
|
||||
[postgre 9.6pdo]
|
||||
driver = pdo
|
||||
dsn = "pgsql:host=127.0.0.1;port=5432;dbname=dibi_test"
|
||||
user = postgres
|
||||
password = postgres
|
||||
system = postgre
|
||||
|
||||
[postgre 13]
|
||||
driver = postgre
|
||||
host = "127.0.0.1"
|
||||
database = dibi_test
|
||||
username = postgres
|
||||
password = postgres
|
||||
port = 5433
|
||||
system = postgre
|
||||
|
||||
[postgre 13pdo]
|
||||
driver = pdo
|
||||
dsn = "pgsql:host=127.0.0.1;port=5433;dbname=dibi_test"
|
||||
user = postgres
|
||||
password = postgres
|
||||
system = postgre
|
||||
|
||||
[sqlsrv]
|
||||
driver = sqlsrv
|
||||
host = "localhost"
|
||||
username = SA
|
||||
password = "YourStrong!Passw0rd"
|
||||
database = dibi_test
|
||||
port = 1433
|
||||
system = sqlsrv
|
||||
|
||||
;[sqlsrv pdo]
|
||||
;driver = pdo
|
||||
;dsn = "sqlsrv:Server=localhost,1433;Database=dibi_test"
|
||||
;user = SA
|
||||
;password = "YourStrong!Passw0rd"
|
||||
;system = sqlsrv
|
@@ -1,38 +0,0 @@
|
||||
[sqlite] ; default
|
||||
driver = sqlite
|
||||
database = :memory:
|
||||
system = sqlite
|
||||
|
||||
[sqlite pdo]
|
||||
driver = pdo
|
||||
dsn = "sqlite::memory:"
|
||||
system = sqlite
|
||||
|
||||
[mysql improved]
|
||||
driver = mysqli
|
||||
host = 127.0.0.1
|
||||
username = root
|
||||
password =
|
||||
charset = utf8
|
||||
system = mysql
|
||||
|
||||
[mysql pdo]
|
||||
driver = pdo
|
||||
dsn = "mysql:host=127.0.0.1"
|
||||
username = root
|
||||
password =
|
||||
system = mysql
|
||||
|
||||
[postgre]
|
||||
driver = postgre
|
||||
host = 127.0.0.1
|
||||
username = postgres
|
||||
password =
|
||||
system = postgre
|
||||
|
||||
[postgre pdo]
|
||||
driver = pdo
|
||||
dsn = "pgsql:host=127.0.0.1;dbname=dibi_test"
|
||||
username = postgres
|
||||
password =
|
||||
system = postgre
|
@@ -63,6 +63,15 @@ test('', function () use ($config) {
|
||||
});
|
||||
|
||||
|
||||
test('', function () use ($config) {
|
||||
$conn = new Connection($config);
|
||||
Assert::true($conn->isConnected());
|
||||
|
||||
$conn->__destruct();
|
||||
Assert::false($conn->isConnected());
|
||||
});
|
||||
|
||||
|
||||
test('', function () use ($config) {
|
||||
Assert::exception(function () use ($config) {
|
||||
new Connection($config + ['onConnect' => '']);
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Tester\Assert;
|
||||
|
@@ -30,42 +30,102 @@ Assert::exception(function () use ($conn) {
|
||||
*/
|
||||
|
||||
|
||||
$conn->begin();
|
||||
Assert::same(3, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||
$conn->query('INSERT INTO [products]', [
|
||||
'title' => 'Test product',
|
||||
]);
|
||||
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||
$conn->rollback();
|
||||
Assert::same(3, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||
|
||||
|
||||
|
||||
|
||||
$conn->begin();
|
||||
$conn->query('INSERT INTO [products]', [
|
||||
'title' => 'Test product',
|
||||
]);
|
||||
$conn->commit();
|
||||
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||
|
||||
|
||||
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->transaction(function () use ($conn) {
|
||||
$conn->query('INSERT INTO [products]', [
|
||||
'title' => 'Test product',
|
||||
]);
|
||||
throw new Exception('my exception');
|
||||
});
|
||||
}, \Throwable::class, 'my exception');
|
||||
|
||||
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||
|
||||
$conn->transaction(function () use ($conn) {
|
||||
test('begin() & rollback()', function () use ($conn) {
|
||||
$conn->begin();
|
||||
Assert::same(3, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||
$conn->query('INSERT INTO [products]', [
|
||||
'title' => 'Test product',
|
||||
]);
|
||||
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||
$conn->rollback();
|
||||
Assert::same(3, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||
});
|
||||
|
||||
Assert::same(5, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||
|
||||
test('begin() & commit()', function () use ($conn) {
|
||||
$conn->begin();
|
||||
$conn->query('INSERT INTO [products]', [
|
||||
'title' => 'Test product',
|
||||
]);
|
||||
$conn->commit();
|
||||
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||
});
|
||||
|
||||
|
||||
test('transaction() fail', function () use ($conn) {
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->transaction(function (Dibi\Connection $connection) {
|
||||
$connection->query('INSERT INTO [products]', [
|
||||
'title' => 'Test product',
|
||||
]);
|
||||
throw new Exception('my exception');
|
||||
});
|
||||
}, Throwable::class, 'my exception');
|
||||
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||
});
|
||||
|
||||
|
||||
test('transaction() success', function () use ($conn) {
|
||||
$conn->transaction(function (Dibi\Connection $connection) {
|
||||
$connection->query('INSERT INTO [products]', [
|
||||
'title' => 'Test product',
|
||||
]);
|
||||
});
|
||||
Assert::same(5, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||
});
|
||||
|
||||
|
||||
test('nested transaction() call fail', function () use ($conn) {
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->transaction(function (Dibi\Connection $connection) {
|
||||
$connection->query('INSERT INTO [products]', [
|
||||
'title' => 'Test product',
|
||||
]);
|
||||
|
||||
$connection->transaction(function (Dibi\Connection $connection2) {
|
||||
$connection2->query('INSERT INTO [products]', [
|
||||
'title' => 'Test product',
|
||||
]);
|
||||
throw new Exception('my exception');
|
||||
});
|
||||
});
|
||||
}, Throwable::class, 'my exception');
|
||||
Assert::same(5, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||
});
|
||||
|
||||
|
||||
test('nested transaction() call success', function () use ($conn) {
|
||||
$conn->transaction(function (Dibi\Connection $connection) {
|
||||
$connection->query('INSERT INTO [products]', [
|
||||
'title' => 'Test product',
|
||||
]);
|
||||
|
||||
$connection->transaction(function (Dibi\Connection $connection2) {
|
||||
$connection2->query('INSERT INTO [products]', [
|
||||
'title' => 'Test product',
|
||||
]);
|
||||
});
|
||||
});
|
||||
Assert::same(7, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||
});
|
||||
|
||||
|
||||
test('begin(), commit() & rollback() calls are forbidden in transaction()', function () use ($conn) {
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->transaction(function (Dibi\Connection $connection) {
|
||||
$connection->begin();
|
||||
});
|
||||
}, LogicException::class, Dibi\Connection::class . '::begin() call is forbidden inside a transaction() callback');
|
||||
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->transaction(function (Dibi\Connection $connection) {
|
||||
$connection->commit();
|
||||
});
|
||||
}, LogicException::class, Dibi\Connection::class . '::commit() call is forbidden inside a transaction() callback');
|
||||
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->transaction(function (Dibi\Connection $connection) {
|
||||
$connection->rollback();
|
||||
});
|
||||
}, LogicException::class, Dibi\Connection::class . '::rollback() call is forbidden inside a transaction() callback');
|
||||
});
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Dibi\Row;
|
||||
|
@@ -14,8 +14,6 @@ Assert::same('1978-01-23 11:40:00.000000', (string) new DateTime(254400000));
|
||||
Assert::same('1978-01-23 11:40:00.000000', (string) (new DateTime)->setTimestamp(254400000));
|
||||
Assert::same(254400000, (new DateTime(254400000))->getTimestamp());
|
||||
|
||||
Assert::same('2050-08-13 11:40:00.000000', (string) new DateTime(2544000000));
|
||||
Assert::same('2050-08-13 11:40:00.000000', (string) (new DateTime)->setTimestamp(2544000000));
|
||||
Assert::same(is_int(2544000000) ? 2544000000 : '2544000000', (new DateTime(2544000000))->getTimestamp()); // 64 bit
|
||||
|
||||
Assert::same('1978-05-05 00:00:00.000000', (string) new DateTime('1978-05-05'));
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Dibi\Fluent;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Tester\Assert;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Tester\Assert;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Tester\Assert;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Tester\Assert;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Dibi\Helpers;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Dibi\Helpers;
|
||||
|
@@ -13,6 +13,7 @@ function buildPdoDriver(?int $errorMode)
|
||||
if ($errorMode !== null) {
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, $errorMode);
|
||||
}
|
||||
|
||||
new Dibi\Drivers\PdoDriver(['resource' => $pdo]);
|
||||
}
|
||||
|
||||
|
@@ -32,7 +32,7 @@ Assert::same(
|
||||
);
|
||||
|
||||
|
||||
if (!in_array($config['driver'], ['sqlite', 'pdo', 'sqlsrv'], true)) {
|
||||
if (!in_array($config['driver'], ['sqlite', 'sqlite3', 'pdo', 'sqlsrv'], true)) {
|
||||
Assert::same(
|
||||
['products.product_id', 'orders.order_id', 'customers.name', 'xXx'],
|
||||
$info->getColumnNames(true)
|
||||
@@ -43,7 +43,7 @@ if (!in_array($config['driver'], ['sqlite', 'pdo', 'sqlsrv'], true)) {
|
||||
$columns = $info->getColumns();
|
||||
|
||||
Assert::same('product_id', $columns[0]->getName());
|
||||
if (!in_array($config['driver'], ['sqlite', 'pdo', 'sqlsrv'], true)) {
|
||||
if (!in_array($config['driver'], ['sqlite', 'sqlite3', 'pdo', 'sqlsrv'], true)) {
|
||||
Assert::same('products', $columns[0]->getTableName());
|
||||
}
|
||||
Assert::null($columns[0]->getVendorInfo('xxx'));
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Dibi\Type;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Tester\Assert;
|
||||
|
@@ -35,6 +35,10 @@ Assert::error(function () use ($row) {
|
||||
Assert::false(isset($row->missing));
|
||||
Assert::false(isset($row['missing']));
|
||||
|
||||
// ??
|
||||
Assert::same(123, $row->missing ?? 123);
|
||||
Assert::same(123, $row['missing'] ?? 123);
|
||||
|
||||
|
||||
// suggestions
|
||||
Assert::error(function () use ($row) {
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Tester\Assert;
|
||||
|
41
tests/dibi/Translator.enums.phpt
Normal file
41
tests/dibi/Translator.enums.phpt
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @phpVersion 8.1
|
||||
* @dataProvider ../databases.ini
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Tester\Assert;
|
||||
|
||||
require __DIR__ . '/bootstrap.php';
|
||||
|
||||
$conn = new Dibi\Connection($config);
|
||||
$translator = new Dibi\Translator($conn);
|
||||
|
||||
|
||||
enum EnumInt: int
|
||||
{
|
||||
case One = 1;
|
||||
}
|
||||
|
||||
enum EnumString: string
|
||||
{
|
||||
case One = 'one';
|
||||
}
|
||||
|
||||
enum PureEnum
|
||||
{
|
||||
case One;
|
||||
}
|
||||
|
||||
|
||||
Assert::equal('1', $translator->formatValue(EnumInt::One, null));
|
||||
|
||||
Assert::equal(match ($config['driver']) {
|
||||
'sqlsrv' => "N'one'",
|
||||
default => "'one'",
|
||||
}, $translator->formatValue(EnumString::One, null));
|
||||
|
||||
Assert::equal('**Unexpected PureEnum**', $translator->formatValue(PureEnum::One, null));
|
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @dataProvider ../databases.ini
|
||||
* @dataProvider ../databases.ini !=sqlsrv
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// The Nette Tester command-line runner can be
|
||||
@@ -18,11 +19,15 @@ date_default_timezone_set('Europe/Prague');
|
||||
// load connection
|
||||
try {
|
||||
$config = Tester\Environment::loadData();
|
||||
} catch (Exception $e) {
|
||||
} catch (Throwable $e) {
|
||||
$config = parse_ini_file(__DIR__ . '/../databases.ini', true);
|
||||
$config = reset($config);
|
||||
}
|
||||
|
||||
if (isset($config['port'])) {
|
||||
$config['port'] = (int) $config['port'];
|
||||
}
|
||||
|
||||
|
||||
// lock
|
||||
define('TEMP_DIR', __DIR__ . '/../tmp');
|
||||
|
@@ -36,7 +36,7 @@ Assert::same('SELECT', $e->getSql());
|
||||
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
$conn->query('INSERT INTO products (product_id, title) VALUES (1, "New")');
|
||||
}, Dibi\UniqueConstraintViolationException::class, "%a?%Duplicate entry '1' for key 'PRIMARY'", 1062);
|
||||
}, Dibi\UniqueConstraintViolationException::class, "%a?%Duplicate entry '1' for key '%a?%PRIMARY'", 1062);
|
||||
|
||||
Assert::same("INSERT INTO products (product_id, title) VALUES (1, 'New')", $e->getSql());
|
||||
|
||||
|
@@ -31,7 +31,7 @@ Assert::same("INSERT INTO products (product_id, title) VALUES (1, 'New')", $e->g
|
||||
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
$conn->query('INSERT INTO products (title) VALUES (NULL)');
|
||||
}, Dibi\NotNullConstraintViolationException::class, '%a?%null value in column "title" violates not-null constraint%A?%', '23502');
|
||||
}, Dibi\NotNullConstraintViolationException::class, '%a?%null value in column "title"%a%violates not-null constraint%A?%', '23502');
|
||||
|
||||
Assert::same('INSERT INTO products (title) VALUES (NULL)', $e->getSql());
|
||||
|
||||
|
@@ -12,5 +12,5 @@ extension=php_pdo_pgsql.dll
|
||||
extension=php_pdo_sqlite.dll
|
||||
extension=php_pgsql.dll
|
||||
extension=php_sqlite3.dll
|
||||
extension=php_sqlsrv_71_ts.dll
|
||||
extension=php_sqlsrv_ts.dll
|
||||
extension=php_odbc.dll
|
||||
|
Reference in New Issue
Block a user