|
@@ -1,40 +1,39 @@
|
|
|
<?php
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * This file is part of the "dibi" - smart database abstraction layer.
|
|
|
|
|
|
|
+ * This file is part of the Dibi, smart database abstraction layer (https://dibiphp.com)
|
|
|
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
|
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
|
|
+declare(strict_types=1);
|
|
|
|
|
+
|
|
|
namespace Dibi;
|
|
namespace Dibi;
|
|
|
|
|
|
|
|
use Traversable;
|
|
use Traversable;
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * dibi connection.
|
|
|
|
|
|
|
+ * Dibi connection.
|
|
|
*
|
|
*
|
|
|
* @property-read int $affectedRows
|
|
* @property-read int $affectedRows
|
|
|
* @property-read int $insertId
|
|
* @property-read int $insertId
|
|
|
*/
|
|
*/
|
|
|
-class Connection
|
|
|
|
|
|
|
+class Connection implements IConnection
|
|
|
{
|
|
{
|
|
|
use Strict;
|
|
use Strict;
|
|
|
|
|
|
|
|
/** @var array of function (Event $event); Occurs after query is executed */
|
|
/** @var array of function (Event $event); Occurs after query is executed */
|
|
|
- public $onEvent;
|
|
|
|
|
|
|
+ public $onEvent = [];
|
|
|
|
|
|
|
|
/** @var array Current connection configuration */
|
|
/** @var array Current connection configuration */
|
|
|
private $config;
|
|
private $config;
|
|
|
|
|
|
|
|
- /** @var Driver */
|
|
|
|
|
|
|
+ /** @var Driver|null */
|
|
|
private $driver;
|
|
private $driver;
|
|
|
|
|
|
|
|
- /** @var Translator */
|
|
|
|
|
|
|
+ /** @var Translator|null */
|
|
|
private $translator;
|
|
private $translator;
|
|
|
|
|
|
|
|
- /** @var bool Is connected? */
|
|
|
|
|
- private $connected = false;
|
|
|
|
|
-
|
|
|
|
|
/** @var HashMap Substitutes for identifiers */
|
|
/** @var HashMap Substitutes for identifiers */
|
|
|
private $substitutes;
|
|
private $substitutes;
|
|
|
|
|
|
|
@@ -44,20 +43,22 @@ class Connection
|
|
|
* - lazy (bool) => if true, connection will be established only when required
|
|
* - lazy (bool) => if true, connection will be established only when required
|
|
|
* - result (array) => result set options
|
|
* - result (array) => result set options
|
|
|
* - formatDateTime => date-time format (if empty, DateTime objects will be returned)
|
|
* - formatDateTime => date-time format (if empty, DateTime objects will be returned)
|
|
|
- * - profiler (array or bool)
|
|
|
|
|
|
|
+ * - profiler (array)
|
|
|
* - run (bool) => enable profiler?
|
|
* - run (bool) => enable profiler?
|
|
|
* - file => file to log
|
|
* - file => file to log
|
|
|
* - substitutes (array) => map of driver specific substitutes (under development)
|
|
* - substitutes (array) => map of driver specific substitutes (under development)
|
|
|
- * @param mixed connection parameters
|
|
|
|
|
- * @param string connection name
|
|
|
|
|
|
|
+ * - onConnect (array) => list of SQL queries to execute (by Connection::query()) after connection is established
|
|
|
|
|
+ * @param array $config connection parameters
|
|
|
* @throws Exception
|
|
* @throws Exception
|
|
|
*/
|
|
*/
|
|
|
- public function __construct($config, $name = null)
|
|
|
|
|
|
|
+ public function __construct($config, string $name = null)
|
|
|
{
|
|
{
|
|
|
if (is_string($config)) {
|
|
if (is_string($config)) {
|
|
|
|
|
+ trigger_error(__METHOD__ . '() Configuration should be array.', E_USER_DEPRECATED);
|
|
|
parse_str($config, $config);
|
|
parse_str($config, $config);
|
|
|
|
|
|
|
|
} elseif ($config instanceof Traversable) {
|
|
} elseif ($config instanceof Traversable) {
|
|
|
|
|
+ trigger_error(__METHOD__ . '() Configuration should be array.', E_USER_DEPRECATED);
|
|
|
$tmp = [];
|
|
$tmp = [];
|
|
|
foreach ($config as $key => $val) {
|
|
foreach ($config as $key => $val) {
|
|
|
$tmp[$key] = $val instanceof Traversable ? iterator_to_array($val) : $val;
|
|
$tmp[$key] = $val instanceof Traversable ? iterator_to_array($val) : $val;
|
|
@@ -65,7 +66,7 @@ class Connection
|
|
|
$config = $tmp;
|
|
$config = $tmp;
|
|
|
|
|
|
|
|
} elseif (!is_array($config)) {
|
|
} elseif (!is_array($config)) {
|
|
|
- throw new \InvalidArgumentException('Configuration must be array, string or object.');
|
|
|
|
|
|
|
+ throw new \InvalidArgumentException('Configuration must be array.');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Helpers::alias($config, 'username', 'user');
|
|
Helpers::alias($config, 'username', 'user');
|
|
@@ -73,52 +74,27 @@ class Connection
|
|
|
Helpers::alias($config, 'host', 'hostname');
|
|
Helpers::alias($config, 'host', 'hostname');
|
|
|
Helpers::alias($config, 'result|formatDate', 'resultDate');
|
|
Helpers::alias($config, 'result|formatDate', 'resultDate');
|
|
|
Helpers::alias($config, 'result|formatDateTime', 'resultDateTime');
|
|
Helpers::alias($config, 'result|formatDateTime', 'resultDateTime');
|
|
|
-
|
|
|
|
|
- if (!isset($config['driver'])) {
|
|
|
|
|
- $config['driver'] = \dibi::$defaultDriver;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if ($config['driver'] instanceof Driver) {
|
|
|
|
|
- $this->driver = $config['driver'];
|
|
|
|
|
- $config['driver'] = get_class($this->driver);
|
|
|
|
|
- } elseif (is_subclass_of($config['driver'], 'Dibi\Driver')) {
|
|
|
|
|
- $this->driver = new $config['driver'];
|
|
|
|
|
- } else {
|
|
|
|
|
- $class = preg_replace(['#\W#', '#sql#'], ['_', 'Sql'], ucfirst(strtolower($config['driver'])));
|
|
|
|
|
- $class = "Dibi\\Drivers\\{$class}Driver";
|
|
|
|
|
- if (!class_exists($class)) {
|
|
|
|
|
- throw new Exception("Unable to create instance of dibi driver '$class'.");
|
|
|
|
|
- }
|
|
|
|
|
- $this->driver = new $class;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ $config['driver'] = $config['driver'] ?? 'mysqli';
|
|
|
$config['name'] = $name;
|
|
$config['name'] = $name;
|
|
|
$this->config = $config;
|
|
$this->config = $config;
|
|
|
|
|
|
|
|
// profiler
|
|
// profiler
|
|
|
- $profilerCfg = &$config['profiler'];
|
|
|
|
|
- if (is_scalar($profilerCfg)) {
|
|
|
|
|
- $profilerCfg = ['run' => (bool) $profilerCfg];
|
|
|
|
|
|
|
+ if (isset($config['profiler']['file']) && (!isset($config['profiler']['run']) || $config['profiler']['run'])) {
|
|
|
|
|
+ $filter = $config['profiler']['filter'] ?? Event::QUERY;
|
|
|
|
|
+ $this->onEvent[] = [new Loggers\FileLogger($config['profiler']['file'], $filter), 'logEvent'];
|
|
|
}
|
|
}
|
|
|
- if (!empty($profilerCfg['run'])) {
|
|
|
|
|
- $filter = isset($profilerCfg['filter']) ? $profilerCfg['filter'] : Event::QUERY;
|
|
|
|
|
|
|
|
|
|
- if (isset($profilerCfg['file'])) {
|
|
|
|
|
- $this->onEvent[] = [new Loggers\FileLogger($profilerCfg['file'], $filter), 'logEvent'];
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (Loggers\FirePhpLogger::isAvailable()) {
|
|
|
|
|
- $this->onEvent[] = [new Loggers\FirePhpLogger($filter), 'logEvent'];
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- $this->substitutes = new HashMap(function ($expr) { return ":$expr:"; });
|
|
|
|
|
|
|
+ $this->substitutes = new HashMap(function (string $expr) { return ":$expr:"; });
|
|
|
if (!empty($config['substitutes'])) {
|
|
if (!empty($config['substitutes'])) {
|
|
|
foreach ($config['substitutes'] as $key => $value) {
|
|
foreach ($config['substitutes'] as $key => $value) {
|
|
|
$this->substitutes->$key = $value;
|
|
$this->substitutes->$key = $value;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if (isset($config['onConnect']) && !is_array($config['onConnect'])) {
|
|
|
|
|
+ throw new \InvalidArgumentException("Configuration option 'onConnect' must be array.");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (empty($config['lazy'])) {
|
|
if (empty($config['lazy'])) {
|
|
|
$this->connect();
|
|
$this->connect();
|
|
|
}
|
|
}
|
|
@@ -127,29 +103,51 @@ class Connection
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Automatically frees the resources allocated for this result set.
|
|
* Automatically frees the resources allocated for this result set.
|
|
|
- * @return void
|
|
|
|
|
*/
|
|
*/
|
|
|
public function __destruct()
|
|
public function __destruct()
|
|
|
{
|
|
{
|
|
|
- // disconnects and rolls back transaction - do not rely on auto-disconnect and rollback!
|
|
|
|
|
- $this->connected && $this->driver->getResource() && $this->disconnect();
|
|
|
|
|
|
|
+ if ($this->driver && $this->driver->getResource()) {
|
|
|
|
|
+ $this->disconnect();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Connects to a database.
|
|
* Connects to a database.
|
|
|
- * @return void
|
|
|
|
|
*/
|
|
*/
|
|
|
- final public function connect()
|
|
|
|
|
|
|
+ final public function connect(): void
|
|
|
{
|
|
{
|
|
|
|
|
+ if ($this->config['driver'] instanceof Driver) {
|
|
|
|
|
+ $this->driver = $this->config['driver'];
|
|
|
|
|
+ return;
|
|
|
|
|
+
|
|
|
|
|
+ } elseif (is_subclass_of($this->config['driver'], Driver::class)) {
|
|
|
|
|
+ $class = $this->config['driver'];
|
|
|
|
|
+
|
|
|
|
|
+ } else {
|
|
|
|
|
+ $class = preg_replace(['#\W#', '#sql#'], ['_', 'Sql'], ucfirst(strtolower($this->config['driver'])));
|
|
|
|
|
+ $class = "Dibi\\Drivers\\{$class}Driver";
|
|
|
|
|
+ if (!class_exists($class)) {
|
|
|
|
|
+ throw new Exception("Unable to create instance of Dibi driver '$class'.");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
$event = $this->onEvent ? new Event($this, Event::CONNECT) : null;
|
|
$event = $this->onEvent ? new Event($this, Event::CONNECT) : null;
|
|
|
try {
|
|
try {
|
|
|
- $this->driver->connect($this->config);
|
|
|
|
|
- $this->connected = true;
|
|
|
|
|
- $event && $this->onEvent($event->done());
|
|
|
|
|
|
|
+ $this->driver = new $class($this->config);
|
|
|
|
|
+ if ($event) {
|
|
|
|
|
+ $this->onEvent($event->done());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isset($this->config['onConnect'])) {
|
|
|
|
|
+ foreach ($this->config['onConnect'] as $sql) {
|
|
|
|
|
+ $this->query($sql);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- } catch (Exception $e) {
|
|
|
|
|
- $event && $this->onEvent($event->done($e));
|
|
|
|
|
|
|
+ } catch (DriverException $e) {
|
|
|
|
|
+ if ($event) {
|
|
|
|
|
+ $this->onEvent($event->done($e));
|
|
|
|
|
+ }
|
|
|
throw $e;
|
|
throw $e;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -157,99 +155,78 @@ class Connection
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Disconnects from a database.
|
|
* Disconnects from a database.
|
|
|
- * @return void
|
|
|
|
|
*/
|
|
*/
|
|
|
- final public function disconnect()
|
|
|
|
|
|
|
+ final public function disconnect(): void
|
|
|
{
|
|
{
|
|
|
- $this->driver->disconnect();
|
|
|
|
|
- $this->connected = false;
|
|
|
|
|
|
|
+ if ($this->driver) {
|
|
|
|
|
+ $this->driver->disconnect();
|
|
|
|
|
+ $this->driver = null;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Returns true when connection was established.
|
|
* Returns true when connection was established.
|
|
|
- * @return bool
|
|
|
|
|
*/
|
|
*/
|
|
|
- final public function isConnected()
|
|
|
|
|
|
|
+ final public function isConnected(): bool
|
|
|
{
|
|
{
|
|
|
- return $this->connected;
|
|
|
|
|
|
|
+ return (bool) $this->driver;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Returns configuration variable. If no $key is passed, returns the entire array.
|
|
* Returns configuration variable. If no $key is passed, returns the entire array.
|
|
|
* @see self::__construct
|
|
* @see self::__construct
|
|
|
- * @param string
|
|
|
|
|
- * @param mixed default value to use if key not found
|
|
|
|
|
* @return mixed
|
|
* @return mixed
|
|
|
*/
|
|
*/
|
|
|
- final public function getConfig($key = null, $default = null)
|
|
|
|
|
|
|
+ final public function getConfig(string $key = null, $default = null)
|
|
|
{
|
|
{
|
|
|
- if ($key === null) {
|
|
|
|
|
- return $this->config;
|
|
|
|
|
-
|
|
|
|
|
- } elseif (isset($this->config[$key])) {
|
|
|
|
|
- return $this->config[$key];
|
|
|
|
|
-
|
|
|
|
|
- } else {
|
|
|
|
|
- return $default;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- /** @deprecated */
|
|
|
|
|
- public static function alias(&$config, $key, $alias)
|
|
|
|
|
- {
|
|
|
|
|
- trigger_error(__METHOD__ . '() is deprecated, use Helpers::alias().', E_USER_DEPRECATED);
|
|
|
|
|
- Helpers::alias($config, $key, $alias);
|
|
|
|
|
|
|
+ return $key === null
|
|
|
|
|
+ ? $this->config
|
|
|
|
|
+ : ($this->config[$key] ?? $default);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Returns the driver and connects to a database in lazy mode.
|
|
* Returns the driver and connects to a database in lazy mode.
|
|
|
- * @return Driver
|
|
|
|
|
*/
|
|
*/
|
|
|
- final public function getDriver()
|
|
|
|
|
|
|
+ final public function getDriver(): Driver
|
|
|
{
|
|
{
|
|
|
- $this->connected || $this->connect();
|
|
|
|
|
|
|
+ if (!$this->driver) {
|
|
|
|
|
+ $this->connect();
|
|
|
|
|
+ }
|
|
|
return $this->driver;
|
|
return $this->driver;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Generates (translates) and executes SQL query.
|
|
* Generates (translates) and executes SQL query.
|
|
|
- * @param array|mixed one or more arguments
|
|
|
|
|
- * @return Result|int result set or number of affected rows
|
|
|
|
|
|
|
+ * @param mixed ...$args
|
|
|
* @throws Exception
|
|
* @throws Exception
|
|
|
*/
|
|
*/
|
|
|
- final public function query($args)
|
|
|
|
|
|
|
+ final public function query(...$args): Result
|
|
|
{
|
|
{
|
|
|
- $args = func_get_args();
|
|
|
|
|
return $this->nativeQuery($this->translateArgs($args));
|
|
return $this->nativeQuery($this->translateArgs($args));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Generates SQL query.
|
|
* Generates SQL query.
|
|
|
- * @param array|mixed one or more arguments
|
|
|
|
|
- * @return string
|
|
|
|
|
|
|
+ * @param mixed ...$args
|
|
|
* @throws Exception
|
|
* @throws Exception
|
|
|
*/
|
|
*/
|
|
|
- final public function translate($args)
|
|
|
|
|
|
|
+ final public function translate(...$args): string
|
|
|
{
|
|
{
|
|
|
- $args = func_get_args();
|
|
|
|
|
return $this->translateArgs($args);
|
|
return $this->translateArgs($args);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Generates and prints SQL query.
|
|
* Generates and prints SQL query.
|
|
|
- * @param array|mixed one or more arguments
|
|
|
|
|
- * @return bool
|
|
|
|
|
|
|
+ * @param mixed ...$args
|
|
|
*/
|
|
*/
|
|
|
- final public function test($args)
|
|
|
|
|
|
|
+ final public function test(...$args): bool
|
|
|
{
|
|
{
|
|
|
- $args = func_get_args();
|
|
|
|
|
try {
|
|
try {
|
|
|
Helpers::dump($this->translateArgs($args));
|
|
Helpers::dump($this->translateArgs($args));
|
|
|
return true;
|
|
return true;
|
|
@@ -267,25 +244,23 @@ class Connection
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Generates (translates) and returns SQL query as DataSource.
|
|
* Generates (translates) and returns SQL query as DataSource.
|
|
|
- * @param array|mixed one or more arguments
|
|
|
|
|
- * @return DataSource
|
|
|
|
|
|
|
+ * @param mixed ...$args
|
|
|
* @throws Exception
|
|
* @throws Exception
|
|
|
*/
|
|
*/
|
|
|
- final public function dataSource($args)
|
|
|
|
|
|
|
+ final public function dataSource(...$args): DataSource
|
|
|
{
|
|
{
|
|
|
- $args = func_get_args();
|
|
|
|
|
return new DataSource($this->translateArgs($args), $this);
|
|
return new DataSource($this->translateArgs($args), $this);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Generates SQL query.
|
|
* Generates SQL query.
|
|
|
- * @param array
|
|
|
|
|
- * @return string
|
|
|
|
|
*/
|
|
*/
|
|
|
- protected function translateArgs($args)
|
|
|
|
|
|
|
+ protected function translateArgs(array $args): string
|
|
|
{
|
|
{
|
|
|
- $this->connected || $this->connect();
|
|
|
|
|
|
|
+ if (!$this->driver) {
|
|
|
|
|
+ $this->connect();
|
|
|
|
|
+ }
|
|
|
if (!$this->translator) {
|
|
if (!$this->translator) {
|
|
|
$this->translator = new Translator($this);
|
|
$this->translator = new Translator($this);
|
|
|
}
|
|
}
|
|
@@ -296,45 +271,45 @@ class Connection
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Executes the SQL query.
|
|
* Executes the SQL query.
|
|
|
- * @param string SQL statement.
|
|
|
|
|
- * @return Result|int result set or number of affected rows
|
|
|
|
|
* @throws Exception
|
|
* @throws Exception
|
|
|
*/
|
|
*/
|
|
|
- final public function nativeQuery($sql)
|
|
|
|
|
|
|
+ final public function nativeQuery(string $sql): Result
|
|
|
{
|
|
{
|
|
|
- $this->connected || $this->connect();
|
|
|
|
|
|
|
+ if (!$this->driver) {
|
|
|
|
|
+ $this->connect();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
\dibi::$sql = $sql;
|
|
\dibi::$sql = $sql;
|
|
|
$event = $this->onEvent ? new Event($this, Event::QUERY, $sql) : null;
|
|
$event = $this->onEvent ? new Event($this, Event::QUERY, $sql) : null;
|
|
|
try {
|
|
try {
|
|
|
$res = $this->driver->query($sql);
|
|
$res = $this->driver->query($sql);
|
|
|
|
|
|
|
|
- } catch (Exception $e) {
|
|
|
|
|
- $event && $this->onEvent($event->done($e));
|
|
|
|
|
|
|
+ } catch (DriverException $e) {
|
|
|
|
|
+ if ($event) {
|
|
|
|
|
+ $this->onEvent($event->done($e));
|
|
|
|
|
+ }
|
|
|
throw $e;
|
|
throw $e;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if ($res) {
|
|
|
|
|
- $res = $this->createResultSet($res);
|
|
|
|
|
- } else {
|
|
|
|
|
- $res = $this->driver->getAffectedRows();
|
|
|
|
|
|
|
+ $res = $this->createResultSet($res ?: new Drivers\NoDataResult(max(0, $this->driver->getAffectedRows())));
|
|
|
|
|
+ if ($event) {
|
|
|
|
|
+ $this->onEvent($event->done($res));
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- $event && $this->onEvent($event->done($res));
|
|
|
|
|
return $res;
|
|
return $res;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
|
|
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
|
|
|
- * @return int number of rows
|
|
|
|
|
* @throws Exception
|
|
* @throws Exception
|
|
|
*/
|
|
*/
|
|
|
- public function getAffectedRows()
|
|
|
|
|
|
|
+ public function getAffectedRows(): int
|
|
|
{
|
|
{
|
|
|
- $this->connected || $this->connect();
|
|
|
|
|
|
|
+ if (!$this->driver) {
|
|
|
|
|
+ $this->connect();
|
|
|
|
|
+ }
|
|
|
$rows = $this->driver->getAffectedRows();
|
|
$rows = $this->driver->getAffectedRows();
|
|
|
- if (!is_int($rows) || $rows < 0) {
|
|
|
|
|
|
|
+ if ($rows === null || $rows < 0) {
|
|
|
throw new Exception('Cannot retrieve number of affected rows.');
|
|
throw new Exception('Cannot retrieve number of affected rows.');
|
|
|
}
|
|
}
|
|
|
return $rows;
|
|
return $rows;
|
|
@@ -344,7 +319,7 @@ class Connection
|
|
|
/**
|
|
/**
|
|
|
* @deprecated
|
|
* @deprecated
|
|
|
*/
|
|
*/
|
|
|
- public function affectedRows()
|
|
|
|
|
|
|
+ public function affectedRows(): int
|
|
|
{
|
|
{
|
|
|
trigger_error(__METHOD__ . '() is deprecated, use getAffectedRows()', E_USER_DEPRECATED);
|
|
trigger_error(__METHOD__ . '() is deprecated, use getAffectedRows()', E_USER_DEPRECATED);
|
|
|
return $this->getAffectedRows();
|
|
return $this->getAffectedRows();
|
|
@@ -353,25 +328,25 @@ class Connection
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
|
|
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
|
|
|
- * @param string optional sequence name
|
|
|
|
|
- * @return int
|
|
|
|
|
* @throws Exception
|
|
* @throws Exception
|
|
|
*/
|
|
*/
|
|
|
- public function getInsertId($sequence = null)
|
|
|
|
|
|
|
+ public function getInsertId(string $sequence = null): int
|
|
|
{
|
|
{
|
|
|
- $this->connected || $this->connect();
|
|
|
|
|
|
|
+ if (!$this->driver) {
|
|
|
|
|
+ $this->connect();
|
|
|
|
|
+ }
|
|
|
$id = $this->driver->getInsertId($sequence);
|
|
$id = $this->driver->getInsertId($sequence);
|
|
|
if ($id < 1) {
|
|
if ($id < 1) {
|
|
|
throw new Exception('Cannot retrieve last generated ID.');
|
|
throw new Exception('Cannot retrieve last generated ID.');
|
|
|
}
|
|
}
|
|
|
- return Helpers::intVal($id);
|
|
|
|
|
|
|
+ return $id;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* @deprecated
|
|
* @deprecated
|
|
|
*/
|
|
*/
|
|
|
- public function insertId($sequence = null)
|
|
|
|
|
|
|
+ public function insertId(string $sequence = null): int
|
|
|
{
|
|
{
|
|
|
trigger_error(__METHOD__ . '() is deprecated, use getInsertId()', E_USER_DEPRECATED);
|
|
trigger_error(__METHOD__ . '() is deprecated, use getInsertId()', E_USER_DEPRECATED);
|
|
|
return $this->getInsertId($sequence);
|
|
return $this->getInsertId($sequence);
|
|
@@ -380,19 +355,23 @@ class Connection
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Begins a transaction (if supported).
|
|
* Begins a transaction (if supported).
|
|
|
- * @param string optional savepoint name
|
|
|
|
|
- * @return void
|
|
|
|
|
*/
|
|
*/
|
|
|
- public function begin($savepoint = null)
|
|
|
|
|
|
|
+ public function begin(string $savepoint = null): void
|
|
|
{
|
|
{
|
|
|
- $this->connected || $this->connect();
|
|
|
|
|
|
|
+ if (!$this->driver) {
|
|
|
|
|
+ $this->connect();
|
|
|
|
|
+ }
|
|
|
$event = $this->onEvent ? new Event($this, Event::BEGIN, $savepoint) : null;
|
|
$event = $this->onEvent ? new Event($this, Event::BEGIN, $savepoint) : null;
|
|
|
try {
|
|
try {
|
|
|
$this->driver->begin($savepoint);
|
|
$this->driver->begin($savepoint);
|
|
|
- $event && $this->onEvent($event->done());
|
|
|
|
|
|
|
+ if ($event) {
|
|
|
|
|
+ $this->onEvent($event->done());
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- } catch (Exception $e) {
|
|
|
|
|
- $event && $this->onEvent($event->done($e));
|
|
|
|
|
|
|
+ } catch (DriverException $e) {
|
|
|
|
|
+ if ($event) {
|
|
|
|
|
+ $this->onEvent($event->done($e));
|
|
|
|
|
+ }
|
|
|
throw $e;
|
|
throw $e;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -400,19 +379,23 @@ class Connection
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Commits statements in a transaction.
|
|
* Commits statements in a transaction.
|
|
|
- * @param string optional savepoint name
|
|
|
|
|
- * @return void
|
|
|
|
|
*/
|
|
*/
|
|
|
- public function commit($savepoint = null)
|
|
|
|
|
|
|
+ public function commit(string $savepoint = null): void
|
|
|
{
|
|
{
|
|
|
- $this->connected || $this->connect();
|
|
|
|
|
|
|
+ if (!$this->driver) {
|
|
|
|
|
+ $this->connect();
|
|
|
|
|
+ }
|
|
|
$event = $this->onEvent ? new Event($this, Event::COMMIT, $savepoint) : null;
|
|
$event = $this->onEvent ? new Event($this, Event::COMMIT, $savepoint) : null;
|
|
|
try {
|
|
try {
|
|
|
$this->driver->commit($savepoint);
|
|
$this->driver->commit($savepoint);
|
|
|
- $event && $this->onEvent($event->done());
|
|
|
|
|
|
|
+ if ($event) {
|
|
|
|
|
+ $this->onEvent($event->done());
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- } catch (Exception $e) {
|
|
|
|
|
- $event && $this->onEvent($event->done($e));
|
|
|
|
|
|
|
+ } catch (DriverException $e) {
|
|
|
|
|
+ if ($event) {
|
|
|
|
|
+ $this->onEvent($event->done($e));
|
|
|
|
|
+ }
|
|
|
throw $e;
|
|
throw $e;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -420,19 +403,23 @@ class Connection
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Rollback changes in a transaction.
|
|
* Rollback changes in a transaction.
|
|
|
- * @param string optional savepoint name
|
|
|
|
|
- * @return void
|
|
|
|
|
*/
|
|
*/
|
|
|
- public function rollback($savepoint = null)
|
|
|
|
|
|
|
+ public function rollback(string $savepoint = null): void
|
|
|
{
|
|
{
|
|
|
- $this->connected || $this->connect();
|
|
|
|
|
|
|
+ if (!$this->driver) {
|
|
|
|
|
+ $this->connect();
|
|
|
|
|
+ }
|
|
|
$event = $this->onEvent ? new Event($this, Event::ROLLBACK, $savepoint) : null;
|
|
$event = $this->onEvent ? new Event($this, Event::ROLLBACK, $savepoint) : null;
|
|
|
try {
|
|
try {
|
|
|
$this->driver->rollback($savepoint);
|
|
$this->driver->rollback($savepoint);
|
|
|
- $event && $this->onEvent($event->done());
|
|
|
|
|
|
|
+ if ($event) {
|
|
|
|
|
+ $this->onEvent($event->done());
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- } catch (Exception $e) {
|
|
|
|
|
- $event && $this->onEvent($event->done($e));
|
|
|
|
|
|
|
+ } catch (DriverException $e) {
|
|
|
|
|
+ if ($event) {
|
|
|
|
|
+ $this->onEvent($event->done($e));
|
|
|
|
|
+ }
|
|
|
throw $e;
|
|
throw $e;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -440,10 +427,8 @@ class Connection
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Result set factory.
|
|
* Result set factory.
|
|
|
- * @param ResultDriver
|
|
|
|
|
- * @return Result
|
|
|
|
|
*/
|
|
*/
|
|
|
- public function createResultSet(ResultDriver $resultDriver)
|
|
|
|
|
|
|
+ public function createResultSet(ResultDriver $resultDriver): Result
|
|
|
{
|
|
{
|
|
|
$res = new Result($resultDriver);
|
|
$res = new Result($resultDriver);
|
|
|
return $res->setFormat(Type::DATE, $this->config['result']['formatDate'])
|
|
return $res->setFormat(Type::DATE, $this->config['result']['formatDate'])
|
|
@@ -454,62 +439,38 @@ class Connection
|
|
|
/********************* fluent SQL builders ****************d*g**/
|
|
/********************* fluent SQL builders ****************d*g**/
|
|
|
|
|
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * @return Fluent
|
|
|
|
|
- */
|
|
|
|
|
- public function command()
|
|
|
|
|
|
|
+ public function command(): Fluent
|
|
|
{
|
|
{
|
|
|
return new Fluent($this);
|
|
return new Fluent($this);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * @param mixed column name
|
|
|
|
|
- * @return Fluent
|
|
|
|
|
- */
|
|
|
|
|
- public function select($args)
|
|
|
|
|
|
|
+ public function select(...$args): Fluent
|
|
|
{
|
|
{
|
|
|
- $args = func_get_args();
|
|
|
|
|
- return $this->command()->__call('select', $args);
|
|
|
|
|
|
|
+ return $this->command()->select(...$args);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * @param string table
|
|
|
|
|
- * @param array
|
|
|
|
|
- * @return Fluent
|
|
|
|
|
|
|
+ * @param string|string[] $table
|
|
|
*/
|
|
*/
|
|
|
- public function update($table, $args)
|
|
|
|
|
|
|
+ public function update($table, iterable $args): Fluent
|
|
|
{
|
|
{
|
|
|
- if (!(is_array($args) || $args instanceof Traversable)) {
|
|
|
|
|
- throw new \InvalidArgumentException('Arguments must be array or Traversable.');
|
|
|
|
|
- }
|
|
|
|
|
return $this->command()->update('%n', $table)->set($args);
|
|
return $this->command()->update('%n', $table)->set($args);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * @param string table
|
|
|
|
|
- * @param array
|
|
|
|
|
- * @return Fluent
|
|
|
|
|
- */
|
|
|
|
|
- public function insert($table, $args)
|
|
|
|
|
|
|
+ public function insert(string $table, iterable $args): Fluent
|
|
|
{
|
|
{
|
|
|
if ($args instanceof Traversable) {
|
|
if ($args instanceof Traversable) {
|
|
|
$args = iterator_to_array($args);
|
|
$args = iterator_to_array($args);
|
|
|
- } elseif (!is_array($args)) {
|
|
|
|
|
- throw new \InvalidArgumentException('Arguments must be array or Traversable.');
|
|
|
|
|
}
|
|
}
|
|
|
return $this->command()->insert()
|
|
return $this->command()->insert()
|
|
|
->into('%n', $table, '(%n)', array_keys($args))->values('%l', $args);
|
|
->into('%n', $table, '(%n)', array_keys($args))->values('%l', $args);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * @param string table
|
|
|
|
|
- * @return Fluent
|
|
|
|
|
- */
|
|
|
|
|
- public function delete($table)
|
|
|
|
|
|
|
+ public function delete(string $table): Fluent
|
|
|
{
|
|
{
|
|
|
return $this->command()->delete()->from('%n', $table);
|
|
return $this->command()->delete()->from('%n', $table);
|
|
|
}
|
|
}
|
|
@@ -520,9 +481,8 @@ class Connection
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Returns substitution hashmap.
|
|
* Returns substitution hashmap.
|
|
|
- * @return HashMap
|
|
|
|
|
*/
|
|
*/
|
|
|
- public function getSubstitutes()
|
|
|
|
|
|
|
+ public function getSubstitutes(): HashMap
|
|
|
{
|
|
{
|
|
|
return $this->substitutes;
|
|
return $this->substitutes;
|
|
|
}
|
|
}
|
|
@@ -530,13 +490,12 @@ class Connection
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Provides substitution.
|
|
* Provides substitution.
|
|
|
- * @return string
|
|
|
|
|
*/
|
|
*/
|
|
|
- public function substitute($value)
|
|
|
|
|
|
|
+ public function substitute(string $value): string
|
|
|
{
|
|
{
|
|
|
return strpos($value, ':') === false
|
|
return strpos($value, ':') === false
|
|
|
? $value
|
|
? $value
|
|
|
- : preg_replace_callback('#:([^:\s]*):#', function ($m) { return $this->substitutes->{$m[1]}; }, $value);
|
|
|
|
|
|
|
+ : preg_replace_callback('#:([^:\s]*):#', function (array $m) { return $this->substitutes->{$m[1]}; }, $value);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -545,75 +504,71 @@ class Connection
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Executes SQL query and fetch result - shortcut for query() & fetch().
|
|
* Executes SQL query and fetch result - shortcut for query() & fetch().
|
|
|
- * @param array|mixed one or more arguments
|
|
|
|
|
- * @return Row|false
|
|
|
|
|
|
|
+ * @param mixed ...$args
|
|
|
* @throws Exception
|
|
* @throws Exception
|
|
|
*/
|
|
*/
|
|
|
- public function fetch($args)
|
|
|
|
|
|
|
+ public function fetch(...$args): ?Row
|
|
|
{
|
|
{
|
|
|
- $args = func_get_args();
|
|
|
|
|
return $this->query($args)->fetch();
|
|
return $this->query($args)->fetch();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Executes SQL query and fetch results - shortcut for query() & fetchAll().
|
|
* Executes SQL query and fetch results - shortcut for query() & fetchAll().
|
|
|
- * @param array|mixed one or more arguments
|
|
|
|
|
- * @return Row[]
|
|
|
|
|
|
|
+ * @param mixed ...$args
|
|
|
|
|
+ * @return Row[]|array[]
|
|
|
* @throws Exception
|
|
* @throws Exception
|
|
|
*/
|
|
*/
|
|
|
- public function fetchAll($args)
|
|
|
|
|
|
|
+ public function fetchAll(...$args): array
|
|
|
{
|
|
{
|
|
|
- $args = func_get_args();
|
|
|
|
|
return $this->query($args)->fetchAll();
|
|
return $this->query($args)->fetchAll();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
|
|
* Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
|
|
|
- * @param array|mixed one or more arguments
|
|
|
|
|
|
|
+ * @param mixed ...$args
|
|
|
* @return mixed
|
|
* @return mixed
|
|
|
* @throws Exception
|
|
* @throws Exception
|
|
|
*/
|
|
*/
|
|
|
- public function fetchSingle($args)
|
|
|
|
|
|
|
+ public function fetchSingle(...$args)
|
|
|
{
|
|
{
|
|
|
- $args = func_get_args();
|
|
|
|
|
return $this->query($args)->fetchSingle();
|
|
return $this->query($args)->fetchSingle();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
|
|
* Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
|
|
|
- * @param array|mixed one or more arguments
|
|
|
|
|
- * @return array
|
|
|
|
|
|
|
+ * @param mixed ...$args
|
|
|
* @throws Exception
|
|
* @throws Exception
|
|
|
*/
|
|
*/
|
|
|
- public function fetchPairs($args)
|
|
|
|
|
|
|
+ public function fetchPairs(...$args): array
|
|
|
{
|
|
{
|
|
|
- $args = func_get_args();
|
|
|
|
|
return $this->query($args)->fetchPairs();
|
|
return $this->query($args)->fetchPairs();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * @return Literal
|
|
|
|
|
- */
|
|
|
|
|
- public static function literal($value)
|
|
|
|
|
|
|
+ public static function literal(string $value): Literal
|
|
|
{
|
|
{
|
|
|
return new Literal($value);
|
|
return new Literal($value);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+ public static function expression(...$args): Expression
|
|
|
|
|
+ {
|
|
|
|
|
+ return new Expression(...$args);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
/********************* misc ****************d*g**/
|
|
/********************* misc ****************d*g**/
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Import SQL dump from file.
|
|
* Import SQL dump from file.
|
|
|
- * @param string filename
|
|
|
|
|
- * @param callable function (int $count, ?float $percent): void
|
|
|
|
|
|
|
+ * @param callable $onProgress function (int $count, ?float $percent): void
|
|
|
* @return int count of sql commands
|
|
* @return int count of sql commands
|
|
|
*/
|
|
*/
|
|
|
- public function loadFile($file, callable $onProgress = null)
|
|
|
|
|
|
|
+ public function loadFile(string $file, callable $onProgress = null): int
|
|
|
{
|
|
{
|
|
|
return Helpers::loadFromFile($this, $file, $onProgress);
|
|
return Helpers::loadFromFile($this, $file, $onProgress);
|
|
|
}
|
|
}
|
|
@@ -621,12 +576,13 @@ class Connection
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Gets a information about the current database.
|
|
* Gets a information about the current database.
|
|
|
- * @return Reflection\Database
|
|
|
|
|
*/
|
|
*/
|
|
|
- public function getDatabaseInfo()
|
|
|
|
|
|
|
+ public function getDatabaseInfo(): Reflection\Database
|
|
|
{
|
|
{
|
|
|
- $this->connected || $this->connect();
|
|
|
|
|
- return new Reflection\Database($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : null);
|
|
|
|
|
|
|
+ if (!$this->driver) {
|
|
|
|
|
+ $this->connect();
|
|
|
|
|
+ }
|
|
|
|
|
+ return new Reflection\Database($this->driver->getReflector(), $this->config['database'] ?? null);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -648,10 +604,10 @@ class Connection
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
- protected function onEvent($arg)
|
|
|
|
|
|
|
+ protected function onEvent($arg): void
|
|
|
{
|
|
{
|
|
|
- foreach ($this->onEvent ?: [] as $handler) {
|
|
|
|
|
- call_user_func($handler, $arg);
|
|
|
|
|
|
|
+ foreach ($this->onEvent as $handler) {
|
|
|
|
|
+ $handler($arg);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|