Update DB
Remove extends PDO. The class is now a wrapper for PDO. https://github.com/php/php-src/issues/7803
This commit is contained in:
parent
cd975a3855
commit
af7f4845f9
1 changed files with 169 additions and 92 deletions
261
app/Core/DB.php
261
app/Core/DB.php
|
@ -10,14 +10,18 @@ declare(strict_types=1);
|
|||
|
||||
namespace ForkBB\Core;
|
||||
|
||||
use ForkBB\Core\DBStatement;
|
||||
use ForkBB\Core\DB\Statement;
|
||||
use PDO;
|
||||
use PDOStatement;
|
||||
use PDOException;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
class DB extends PDO
|
||||
class DB
|
||||
{
|
||||
/**
|
||||
* @var PDO
|
||||
*/
|
||||
protected $pdo;
|
||||
|
||||
/**
|
||||
* Префикс для таблиц базы
|
||||
* @var string
|
||||
|
@ -30,12 +34,24 @@ class DB extends PDO
|
|||
*/
|
||||
protected $dbType;
|
||||
|
||||
/**
|
||||
* Имя класса для драйвера
|
||||
* @var string
|
||||
*/
|
||||
protected $dbDrvClass;
|
||||
|
||||
/**
|
||||
* Драйвер текущей базы
|
||||
* @var //????
|
||||
*/
|
||||
protected $dbDrv;
|
||||
|
||||
/**
|
||||
* Имя класса для PDOStatement
|
||||
* @var string
|
||||
*/
|
||||
protected $statementClass;
|
||||
|
||||
/**
|
||||
* Количество выполненных запросов
|
||||
* @var int
|
||||
|
@ -54,40 +70,114 @@ class DB extends PDO
|
|||
*/
|
||||
protected $delta = 0;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $pdoMethods = [
|
||||
'beginTransaction' => true,
|
||||
'commit' => true,
|
||||
'errorCode' => true,
|
||||
'errorInfo' => true,
|
||||
'exec' => true,
|
||||
'getAttribute' => true,
|
||||
'getAvailableDrivers' => true,
|
||||
'inTransaction' => true,
|
||||
'lastInsertId' => true,
|
||||
'prepare' => true,
|
||||
'query' => true,
|
||||
'quote' => true,
|
||||
'rollBack' => true,
|
||||
'setAttribute' => true,
|
||||
|
||||
'pgsqlCopyFromArray' => true,
|
||||
'pgsqlCopyFromFile' => true,
|
||||
'pgsqlCopyToArray' => true,
|
||||
'pgsqlCopyToFile' => true,
|
||||
'pgsqlGetNotify' => true,
|
||||
'pgsqlGetPid' => true,
|
||||
'pgsqlLOBCreate' => true,
|
||||
'pgsqlLOBOpen' => true,
|
||||
'pgsqlLOBUnlink' => true,
|
||||
|
||||
'sqliteCreateAggregate' => true,
|
||||
'sqliteCreateCollation' => true,
|
||||
'sqliteCreateFunction' => true,
|
||||
];
|
||||
|
||||
public function __construct(string $dsn, string $username = null, string $password = null, array $options = [], string $prefix = '')
|
||||
{
|
||||
$type = \strstr($dsn, ':', true);
|
||||
$typeU = \ucfirst($type);
|
||||
$dsn = $this->initialConfig($dsn);
|
||||
|
||||
$this->dbPrefix = $prefix;
|
||||
|
||||
list($initSQLCommands, $initFunction) = $this->prepareOptions($options);
|
||||
|
||||
$start = \microtime(true);
|
||||
$this->pdo = new PDO($dsn, $username, $password, $options);
|
||||
|
||||
$this->saveQuery('PDO::__construct()', \microtime(true) - $start, false);
|
||||
|
||||
if (\is_string($initSQLCommands)) {
|
||||
$this->exec($initSQLCommands);
|
||||
}
|
||||
|
||||
if (
|
||||
! $type
|
||||
|| ! \in_array($type, PDO::getAvailableDrivers(), true)
|
||||
|| ! \is_file(__DIR__ . "/DB/{$typeU}.php")
|
||||
null !== $initFunction
|
||||
&& true !== $initFunction($this)
|
||||
) {
|
||||
throw new PDOException("initFunction failure");
|
||||
}
|
||||
|
||||
$this->beginTransaction();
|
||||
}
|
||||
|
||||
protected function initialConfig(string $dsn): string
|
||||
{
|
||||
$type = \strstr($dsn, ':', true);
|
||||
|
||||
if (! \in_array($type, PDO::getAvailableDrivers(), true)) {
|
||||
throw new PDOException("PDO does not have driver for '{$type}'");
|
||||
}
|
||||
|
||||
$typeU = \ucfirst($type);
|
||||
|
||||
if (! \is_file(__DIR__ . "/DB/{$typeU}.php")) {
|
||||
throw new PDOException("Driver isn't found for '$type'");
|
||||
}
|
||||
|
||||
$statement = $typeU . 'Statement' . (\PHP_MAJOR_VERSION < 8 ? '7' : '');
|
||||
$this->dbType = $type;
|
||||
$this->dbDrvClass = "ForkBB\\Core\\DB\\{$typeU}";
|
||||
|
||||
if (\is_file(__DIR__ . "/DB/{$statement}.php")) {
|
||||
$statement = 'ForkBB\\Core\\DB\\' . $statement;
|
||||
if (\is_file(__DIR__ . "/DB/{$typeU}Statement.php")) {
|
||||
$this->statementClass = "ForkBB\\Core\\DB\\{$typeU}Statement}";
|
||||
} else {
|
||||
$statement = DBStatement::class;
|
||||
$this->statementClass = Statement::class;
|
||||
}
|
||||
|
||||
if ('sqlite' === $type) {
|
||||
$dsn = \str_replace('!PATH!', \realpath(__DIR__ . '/../config/db') . '/', $dsn);
|
||||
}
|
||||
|
||||
$this->dbType = $type;
|
||||
$this->dbPrefix = $prefix;
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
protected function prepareOptions(array &$options): array
|
||||
{
|
||||
$result = [
|
||||
0 => null,
|
||||
1 => null,
|
||||
];
|
||||
|
||||
if (isset($options['initSQLCommands'])) {
|
||||
$initSQLCommands = \implode(';', $options['initSQLCommands']);
|
||||
$result[0] = \implode(';', $options['initSQLCommands']);
|
||||
|
||||
unset($options['initSQLCommands']);
|
||||
} else {
|
||||
$initSQLCommands = null;
|
||||
}
|
||||
|
||||
if (isset($options['initFunction'])) {
|
||||
$result[1] = $options['initFunction'];
|
||||
|
||||
unset($options['initFunction']);
|
||||
}
|
||||
|
||||
$options += [
|
||||
|
@ -95,52 +185,14 @@ class DB extends PDO
|
|||
self::ATTR_EMULATE_PREPARES => false,
|
||||
self::ATTR_STRINGIFY_FETCHES => false,
|
||||
self::ATTR_ERRMODE => self::ERRMODE_EXCEPTION,
|
||||
self::ATTR_STATEMENT_CLASS => [$statement, [$this]],
|
||||
];
|
||||
|
||||
$start = \microtime(true);
|
||||
|
||||
parent::__construct($dsn, $username, $password, $options);
|
||||
|
||||
$this->saveQuery('PDO::__construct()', \microtime(true) - $start, false);
|
||||
|
||||
if ($initSQLCommands) {
|
||||
$this->exec($initSQLCommands);
|
||||
}
|
||||
|
||||
$this->beginTransaction();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Передает вызовы методов в драйвер текущей базы
|
||||
*/
|
||||
public function __call(string $name, array $args) /* : mixed */
|
||||
protected function dbStatement(PDOStatement $stmt): Statement
|
||||
{
|
||||
if (empty($this->dbDrv)) {
|
||||
$drv = 'ForkBB\\Core\\DB\\' . \ucfirst($this->dbType);
|
||||
$this->dbDrv = new $drv($this, $this->dbPrefix);
|
||||
}
|
||||
|
||||
return $this->dbDrv->$name(...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод определяет массив ли опций подан на вход
|
||||
*/
|
||||
protected function isOptions(array $options): bool
|
||||
{
|
||||
$verify = [self::ATTR_CURSOR => [self::CURSOR_FWDONLY, self::CURSOR_SCROLL]];
|
||||
|
||||
foreach ($options as $key => $value) {
|
||||
if (
|
||||
! isset($verify[$key])
|
||||
|| ! \in_array($value, $verify[$key], true)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return new {$this->statementClass}($this, $stmt);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -207,7 +259,7 @@ class DB extends PDO
|
|||
/**
|
||||
* Метод возвращает значение из массива параметров по ключу или исключение
|
||||
*/
|
||||
public function getValue(/* mixed */ $key, array $params) /* : mixed */
|
||||
public function getValue(/* int|string */ $key, array $params) /* : mixed */
|
||||
{
|
||||
if (
|
||||
\is_string($key)
|
||||
|
@ -259,6 +311,7 @@ class DB extends PDO
|
|||
if ($add) {
|
||||
++$this->qCount;
|
||||
}
|
||||
|
||||
$this->queries[] = [$query, $time + $this->delta];
|
||||
$this->delta = 0;
|
||||
}
|
||||
|
@ -266,23 +319,29 @@ class DB extends PDO
|
|||
/**
|
||||
* Метод расширяет PDO::exec()
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function exec(/* string */ $query, array $params = []) /* : int|false */
|
||||
public function exec(string $query, array $params = []) /* : int|false */
|
||||
{
|
||||
$map = $this->parse($query, $params);
|
||||
|
||||
if (empty($params)) {
|
||||
$start = \microtime(true);
|
||||
$result = parent::exec($query);
|
||||
$result = $this->pdo->exec($query);
|
||||
|
||||
$this->saveQuery($query, \microtime(true) - $start);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
$start = \microtime(true);
|
||||
$stmt = parent::prepare($query);
|
||||
$stmt = $this->pdo->prepare($query);
|
||||
$this->delta = \microtime(true) - $start;
|
||||
|
||||
if (! $stmt instanceof PDOStatement) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$stmt = $this->dbStatement($stmt);
|
||||
|
||||
$stmt->setMap($map);
|
||||
|
||||
if ($stmt->execute($params)) {
|
||||
|
@ -295,31 +354,20 @@ class DB extends PDO
|
|||
/**
|
||||
* Метод расширяет PDO::prepare()
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function prepare(/* string */ $query, /* array */ $arg1 = null, /* array */ $arg2 = null): PDOStatement
|
||||
public function prepare(string $query, array $params = [], array $options = null): /* : Statement|false */
|
||||
{
|
||||
if (
|
||||
empty($arg1) === empty($arg2)
|
||||
|| ! empty($arg2)
|
||||
) {
|
||||
$params = $arg1;
|
||||
$options = $arg2;
|
||||
} elseif ($this->isOptions($arg1)) {
|
||||
$params = [];
|
||||
$options = $arg1;
|
||||
} else {
|
||||
$params = $arg1;
|
||||
$options = [];
|
||||
}
|
||||
|
||||
$map = $this->parse($query, $params);
|
||||
|
||||
$map = $this->parse($query, $params);
|
||||
$start = \microtime(true);
|
||||
$stmt = parent::prepare($query, $options);
|
||||
$stmt = $this->pdo->prepare($query, $options);
|
||||
$this->delta = \microtime(true) - $start;
|
||||
|
||||
$stmt->setMap($map);
|
||||
if (! $stmt instanceof PDOStatement) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$stmt = $this->dbStatement($stmt);
|
||||
|
||||
$stmt->setMap($map);
|
||||
$stmt->bindValueList($params);
|
||||
|
||||
return $stmt;
|
||||
|
@ -328,8 +376,7 @@ class DB extends PDO
|
|||
/**
|
||||
* Метод расширяет PDO::query()
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function query(string $query, /* mixed */ ...$args) /* : PDOStatement|false */
|
||||
public function query(string $query, /* mixed */ ...$args) /* : Statement|false */
|
||||
{
|
||||
if (
|
||||
isset($args[0])
|
||||
|
@ -343,17 +390,28 @@ class DB extends PDO
|
|||
$map = $this->parse($query, $params);
|
||||
|
||||
if (empty($params)) {
|
||||
$start = \microtime(true);
|
||||
$result = parent::query($query, ...$args);
|
||||
$start = \microtime(true);
|
||||
$stmt = $this->pdo->query($query, ...$args);
|
||||
|
||||
$this->saveQuery($query, \microtime(true) - $start);
|
||||
|
||||
return $result;
|
||||
if (! $stmt instanceof PDOStatement) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->dbStatement($stmt);
|
||||
}
|
||||
|
||||
$start = \microtime(true);
|
||||
$stmt = parent::prepare($query);
|
||||
$stmt = $this->pdo->prepare($query);
|
||||
$this->delta = \microtime(true) - $start;
|
||||
|
||||
if (! $stmt instanceof PDOStatement) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$stmt = $this->dbStatement($stmt);
|
||||
|
||||
$stmt->setMap($map);
|
||||
|
||||
if ($stmt->execute($params)) {
|
||||
|
@ -373,7 +431,8 @@ class DB extends PDO
|
|||
public function beginTransaction(): bool
|
||||
{
|
||||
$start = \microtime(true);
|
||||
$result = parent::beginTransaction();
|
||||
$result = $this->pdo->beginTransaction();
|
||||
|
||||
$this->saveQuery('beginTransaction()', \microtime(true) - $start, false);
|
||||
|
||||
return $result;
|
||||
|
@ -385,7 +444,8 @@ class DB extends PDO
|
|||
public function commit(): bool
|
||||
{
|
||||
$start = \microtime(true);
|
||||
$result = parent::commit();
|
||||
$result = $this->pdo->commit();
|
||||
|
||||
$this->saveQuery('commit()', \microtime(true) - $start, false);
|
||||
|
||||
return $result;
|
||||
|
@ -397,9 +457,26 @@ class DB extends PDO
|
|||
public function rollback(): bool
|
||||
{
|
||||
$start = \microtime(true);
|
||||
$result = parent::rollback();
|
||||
$result = $this->pdo->rollback();
|
||||
|
||||
$this->saveQuery('rollback()', \microtime(true) - $start, false);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Передает вызовы метода в PDO или драйвер текущей базы
|
||||
*/
|
||||
public function __call(string $name, array $args) /* : mixed */
|
||||
{
|
||||
if (isset($this->pdoMethods[$name])) {
|
||||
return $this->pdo->$name(...$args);
|
||||
} elseif (empty($this->dbDrv)) {
|
||||
$this->dbDrv = new {$this->dbDrvClass}($this, $this->dbPrefix);
|
||||
|
||||
// ????? проверка типа
|
||||
}
|
||||
|
||||
return $this->dbDrv->$name(...$args);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue