mirror of
https://github.com/dg/dibi.git
synced 2025-10-18 16:26:17 +02:00
renamed driver classes
This commit is contained in:
360
src/Dibi/Drivers/MySQLi/Connection.php
Normal file
360
src/Dibi/Drivers/MySQLi/Connection.php
Normal file
@@ -0,0 +1,360 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Dibi, smart database abstraction layer (https://dibiphp.com)
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Dibi\Drivers\MySQLi;
|
||||
|
||||
use Dibi;
|
||||
use Dibi\Drivers;
|
||||
use function in_array;
|
||||
use const MYSQLI_REPORT_OFF, MYSQLI_STORE_RESULT, MYSQLI_USE_RESULT, PREG_SET_ORDER;
|
||||
|
||||
|
||||
/**
|
||||
* The driver for MySQL database.
|
||||
*
|
||||
* Driver options:
|
||||
* - host => the MySQL server host name
|
||||
* - port (int) => the port number to attempt to connect to the MySQL server
|
||||
* - socket => the socket or named pipe
|
||||
* - username (or user)
|
||||
* - password (or pass)
|
||||
* - database => the database name to select
|
||||
* - options (array) => array of driver specific constants (MYSQLI_*) and values {@see mysqli_options}
|
||||
* - flags (int) => driver specific constants (MYSQLI_CLIENT_*) {@see mysqli_real_connect}
|
||||
* - charset => character encoding to set (default is utf8)
|
||||
* - persistent (bool) => try to find a persistent link?
|
||||
* - unbuffered (bool) => sends query without fetching and buffering the result rows automatically?
|
||||
* - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
|
||||
* - resource (mysqli) => existing connection resource
|
||||
*/
|
||||
class Connection implements Drivers\Connection
|
||||
{
|
||||
public const ErrorAccessDenied = 1045;
|
||||
public const ErrorDuplicateEntry = 1062;
|
||||
public const ErrorDataTruncated = 1265;
|
||||
|
||||
/** @deprecated use MySqliDriver::ErrorAccessDenied */
|
||||
public const ERROR_ACCESS_DENIED = self::ErrorAccessDenied;
|
||||
|
||||
/** @deprecated use MySqliDriver::ErrorDuplicateEntry */
|
||||
public const ERROR_DUPLICATE_ENTRY = self::ErrorDuplicateEntry;
|
||||
|
||||
/** @deprecated use MySqliDriver::ErrorDataTruncated */
|
||||
public const ERROR_DATA_TRUNCATED = self::ErrorDataTruncated;
|
||||
|
||||
private \mysqli $connection;
|
||||
private bool $buffered = false;
|
||||
|
||||
|
||||
/** @throws Dibi\NotSupportedException */
|
||||
public function __construct(array $config)
|
||||
{
|
||||
if (!extension_loaded('mysqli')) {
|
||||
throw new Dibi\NotSupportedException("PHP extension 'mysqli' is not loaded.");
|
||||
}
|
||||
|
||||
mysqli_report(MYSQLI_REPORT_OFF);
|
||||
if (isset($config['resource']) && $config['resource'] instanceof \mysqli) {
|
||||
$this->connection = $config['resource'];
|
||||
|
||||
} else {
|
||||
// default values
|
||||
$config += [
|
||||
'charset' => 'utf8',
|
||||
'timezone' => date('P'),
|
||||
'username' => ini_get('mysqli.default_user'),
|
||||
'password' => ini_get('mysqli.default_pw'),
|
||||
'socket' => (string) ini_get('mysqli.default_socket'),
|
||||
'port' => null,
|
||||
];
|
||||
if (!isset($config['host'])) {
|
||||
$host = ini_get('mysqli.default_host');
|
||||
if ($host) {
|
||||
$config['host'] = $host;
|
||||
$config['port'] = (int) ini_get('mysqli.default_port');
|
||||
} else {
|
||||
$config['host'] = null;
|
||||
$config['port'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
$foo = &$config['flags'];
|
||||
$foo = &$config['database'];
|
||||
|
||||
$this->connection = mysqli_init();
|
||||
if (isset($config['options'])) {
|
||||
foreach ($config['options'] as $key => $value) {
|
||||
$this->connection->options($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
@$this->connection->real_connect( // intentionally @
|
||||
(empty($config['persistent']) ? '' : 'p:') . $config['host'],
|
||||
$config['username'],
|
||||
$config['password'] ?? '',
|
||||
$config['database'] ?? '',
|
||||
$config['port'] ?? 0,
|
||||
$config['socket'],
|
||||
$config['flags'] ?? 0,
|
||||
);
|
||||
|
||||
if ($this->connection->connect_errno) {
|
||||
throw new Dibi\DriverException($this->connection->connect_error, $this->connection->connect_errno);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($config['charset'])) {
|
||||
if (!@$this->connection->set_charset($config['charset'])) {
|
||||
$this->query("SET NAMES '$config[charset]'");
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($config['sqlmode'])) {
|
||||
$this->query("SET sql_mode='$config[sqlmode]'");
|
||||
}
|
||||
|
||||
if (isset($config['timezone'])) {
|
||||
$this->query("SET time_zone='$config[timezone]'");
|
||||
}
|
||||
|
||||
$this->buffered = empty($config['unbuffered']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disconnects from a database.
|
||||
*/
|
||||
public function disconnect(): void
|
||||
{
|
||||
@$this->connection->close(); // @ - connection can be already disconnected
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pings a server connection, or tries to reconnect if the connection has gone down.
|
||||
*/
|
||||
public function ping(): bool
|
||||
{
|
||||
return $this->connection->ping();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the SQL query.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function query(string $sql): ?Result
|
||||
{
|
||||
$res = @$this->connection->query($sql, $this->buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); // intentionally @
|
||||
|
||||
if ($code = mysqli_errno($this->connection)) {
|
||||
throw static::createException(mysqli_error($this->connection), $code, $sql);
|
||||
|
||||
} elseif ($res instanceof \mysqli_result) {
|
||||
return $this->createResultDriver($res);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static function createException(string $message, int|string $code, string $sql): Dibi\DriverException
|
||||
{
|
||||
if (in_array($code, [1216, 1217, 1451, 1452, 1701], true)) {
|
||||
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
|
||||
|
||||
} elseif (in_array($code, [1062, 1557, 1569, 1586], true)) {
|
||||
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
|
||||
|
||||
} elseif (in_array($code, [1048, 1121, 1138, 1171, 1252, 1263, 1566], true)) {
|
||||
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
|
||||
|
||||
} else {
|
||||
return new Dibi\DriverException($message, $code, $sql);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves information about the most recently executed query.
|
||||
*/
|
||||
public function getInfo(): array
|
||||
{
|
||||
$res = [];
|
||||
preg_match_all('#(.+?): +(\d+) *#', $this->connection->info, $matches, PREG_SET_ORDER);
|
||||
if (preg_last_error()) {
|
||||
throw new Dibi\PcreException;
|
||||
}
|
||||
|
||||
foreach ($matches as $m) {
|
||||
$res[$m[1]] = (int) $m[2];
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
|
||||
*/
|
||||
public function getAffectedRows(): ?int
|
||||
{
|
||||
return $this->connection->affected_rows === -1
|
||||
? null
|
||||
: $this->connection->affected_rows;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
|
||||
*/
|
||||
public function getInsertId(?string $sequence): ?int
|
||||
{
|
||||
return $this->connection->insert_id ?: null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begins a transaction (if supported).
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function begin(?string $savepoint = null): void
|
||||
{
|
||||
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Commits statements in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function commit(?string $savepoint = null): void
|
||||
{
|
||||
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rollback changes in a transaction.
|
||||
* @throws Dibi\DriverException
|
||||
*/
|
||||
public function rollback(?string $savepoint = null): void
|
||||
{
|
||||
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the connection resource.
|
||||
*/
|
||||
public function getResource(): ?\mysqli
|
||||
{
|
||||
try {
|
||||
return @$this->connection->thread_id ? $this->connection : null;
|
||||
} catch (\Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the connection reflector.
|
||||
*/
|
||||
public function getReflector(): Drivers\Engine
|
||||
{
|
||||
return new Drivers\Engines\MySQLEngine($this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Result set driver factory.
|
||||
*/
|
||||
public function createResultDriver(\mysqli_result $result): Result
|
||||
{
|
||||
return new Result($result, $this->buffered);
|
||||
}
|
||||
|
||||
|
||||
/********************* SQL ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Encodes data for use in a SQL statement.
|
||||
*/
|
||||
public function escapeText(string $value): string
|
||||
{
|
||||
return "'" . $this->connection->escape_string($value) . "'";
|
||||
}
|
||||
|
||||
|
||||
public function escapeBinary(string $value): string
|
||||
{
|
||||
return "_binary'" . $this->connection->escape_string($value) . "'";
|
||||
}
|
||||
|
||||
|
||||
public function escapeIdentifier(string $value): string
|
||||
{
|
||||
return '`' . str_replace('`', '``', $value) . '`';
|
||||
}
|
||||
|
||||
|
||||
public function escapeBool(bool $value): string
|
||||
{
|
||||
return $value ? '1' : '0';
|
||||
}
|
||||
|
||||
|
||||
public function escapeDate(\DateTimeInterface $value): string
|
||||
{
|
||||
return $value->format("'Y-m-d'");
|
||||
}
|
||||
|
||||
|
||||
public function escapeDateTime(\DateTimeInterface $value): string
|
||||
{
|
||||
return $value->format("'Y-m-d H:i:s.u'");
|
||||
}
|
||||
|
||||
|
||||
public function escapeDateInterval(\DateInterval $value): string
|
||||
{
|
||||
if ($value->y || $value->m || $value->d) {
|
||||
throw new Dibi\NotSupportedException('Only time interval is supported.');
|
||||
}
|
||||
|
||||
return $value->format("'%r%H:%I:%S.%f'");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Encodes string for use in a LIKE statement.
|
||||
*/
|
||||
public function escapeLike(string $value, int $pos): string
|
||||
{
|
||||
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
|
||||
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Injects LIMIT/OFFSET to the SQL query.
|
||||
*/
|
||||
public function applyLimit(string &$sql, ?int $limit, ?int $offset): void
|
||||
{
|
||||
if ($limit < 0 || $offset < 0) {
|
||||
throw new Dibi\NotSupportedException('Negative offset or limit.');
|
||||
|
||||
} elseif ($limit !== null || $offset) {
|
||||
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
|
||||
$sql .= ' LIMIT ' . ($limit ?? '18446744073709551615')
|
||||
. ($offset ? ' OFFSET ' . $offset : '');
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user