Browse Source

Update DB

Remove extends PDO.
The class is now a wrapper for PDO.
https://github.com/php/php-src/issues/7803
Visman 3 years ago
parent
commit
af7f4845f9
1 changed files with 169 additions and 92 deletions
  1. 169 92
      app/Core/DB.php

+ 169 - 92
app/Core/DB.php

@@ -10,14 +10,18 @@ declare(strict_types=1);
 
 
 namespace ForkBB\Core;
 namespace ForkBB\Core;
 
 
-use ForkBB\Core\DBStatement;
+use ForkBB\Core\DB\Statement;
 use PDO;
 use PDO;
 use PDOStatement;
 use PDOStatement;
 use PDOException;
 use PDOException;
-use ReturnTypeWillChange;
 
 
-class DB extends PDO
+class DB
 {
 {
+    /**
+     * @var PDO
+     */
+    protected $pdo;
+
     /**
     /**
      * Префикс для таблиц базы
      * Префикс для таблиц базы
      * @var string
      * @var string
@@ -30,12 +34,24 @@ class DB extends PDO
      */
      */
     protected $dbType;
     protected $dbType;
 
 
+    /**
+     * Имя класса для драйвера
+     * @var string
+     */
+    protected $dbDrvClass;
+
     /**
     /**
      * Драйвер текущей базы
      * Драйвер текущей базы
      * @var //????
      * @var //????
      */
      */
     protected $dbDrv;
     protected $dbDrv;
 
 
+    /**
+     * Имя класса для PDOStatement
+     * @var string
+     */
+    protected $statementClass;
+
     /**
     /**
      * Количество выполненных запросов
      * Количество выполненных запросов
      * @var int
      * @var int
@@ -54,40 +70,114 @@ class DB extends PDO
      */
      */
     protected $delta = 0;
     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 = '')
     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 (
         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'");
             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 {
         } else {
-            $statement = DBStatement::class;
+            $this->statementClass = Statement::class;
         }
         }
 
 
         if ('sqlite' === $type) {
         if ('sqlite' === $type) {
             $dsn = \str_replace('!PATH!', \realpath(__DIR__ . '/../config/db') . '/', $dsn);
             $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'])) {
         if (isset($options['initSQLCommands'])) {
-            $initSQLCommands = \implode(';', $options['initSQLCommands']);
+            $result[0] = \implode(';', $options['initSQLCommands']);
 
 
             unset($options['initSQLCommands']);
             unset($options['initSQLCommands']);
-        } else {
-            $initSQLCommands = null;
+        }
+
+        if (isset($options['initFunction'])) {
+            $result[1] = $options['initFunction'];
+
+            unset($options['initFunction']);
         }
         }
 
 
         $options += [
         $options += [
@@ -95,52 +185,14 @@ class DB extends PDO
             self::ATTR_EMULATE_PREPARES   => false,
             self::ATTR_EMULATE_PREPARES   => false,
             self::ATTR_STRINGIFY_FETCHES  => false,
             self::ATTR_STRINGIFY_FETCHES  => false,
             self::ATTR_ERRMODE            => self::ERRMODE_EXCEPTION,
             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();
-    }
-
-    /**
-     * Передает вызовы методов в драйвер текущей базы
-     */
-    public function __call(string $name, array $args) /* : mixed */
-    {
-        if (empty($this->dbDrv)) {
-            $drv = 'ForkBB\\Core\\DB\\' . \ucfirst($this->dbType);
-            $this->dbDrv = new $drv($this, $this->dbPrefix);
-        }
-
-        return $this->dbDrv->$name(...$args);
+        return $result;
     }
     }
 
 
-    /**
-     * Метод определяет массив ли опций подан на вход
-     */
-    protected function isOptions(array $options): bool
+    protected function dbStatement(PDOStatement $stmt): Statement
     {
     {
-        $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 (
         if (
             \is_string($key)
             \is_string($key)
@@ -259,6 +311,7 @@ class DB extends PDO
         if ($add) {
         if ($add) {
             ++$this->qCount;
             ++$this->qCount;
         }
         }
+
         $this->queries[] = [$query, $time + $this->delta];
         $this->queries[] = [$query, $time + $this->delta];
         $this->delta     = 0;
         $this->delta     = 0;
     }
     }
@@ -266,23 +319,29 @@ class DB extends PDO
     /**
     /**
      * Метод расширяет PDO::exec()
      * Метод расширяет 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);
         $map = $this->parse($query, $params);
 
 
         if (empty($params)) {
         if (empty($params)) {
             $start  = \microtime(true);
             $start  = \microtime(true);
-            $result = parent::exec($query);
+            $result = $this->pdo->exec($query);
+
             $this->saveQuery($query, \microtime(true) - $start);
             $this->saveQuery($query, \microtime(true) - $start);
 
 
             return $result;
             return $result;
         }
         }
 
 
         $start       = \microtime(true);
         $start       = \microtime(true);
-        $stmt        = parent::prepare($query);
+        $stmt        = $this->pdo->prepare($query);
         $this->delta = \microtime(true) - $start;
         $this->delta = \microtime(true) - $start;
 
 
+        if (! $stmt instanceof PDOStatement) {
+            return false;
+        }
+
+        $stmt = $this->dbStatement($stmt);
+
         $stmt->setMap($map);
         $stmt->setMap($map);
 
 
         if ($stmt->execute($params)) {
         if ($stmt->execute($params)) {
@@ -295,31 +354,20 @@ class DB extends PDO
     /**
     /**
      * Метод расширяет PDO::prepare()
      * Метод расширяет 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);
         $start       = \microtime(true);
-        $stmt        = parent::prepare($query, $options);
+        $stmt        = $this->pdo->prepare($query, $options);
         $this->delta = \microtime(true) - $start;
         $this->delta = \microtime(true) - $start;
 
 
-        $stmt->setMap($map);
+        if (! $stmt instanceof PDOStatement) {
+            return false;
+        }
+
+        $stmt = $this->dbStatement($stmt);
 
 
+        $stmt->setMap($map);
         $stmt->bindValueList($params);
         $stmt->bindValueList($params);
 
 
         return $stmt;
         return $stmt;
@@ -328,8 +376,7 @@ class DB extends PDO
     /**
     /**
      * Метод расширяет PDO::query()
      * Метод расширяет PDO::query()
      */
      */
-    #[ReturnTypeWillChange]
-    public function query(string $query, /* mixed */ ...$args) /* : PDOStatement|false */
+    public function query(string $query, /* mixed */ ...$args) /* : Statement|false */
     {
     {
         if (
         if (
             isset($args[0])
             isset($args[0])
@@ -343,17 +390,28 @@ class DB extends PDO
         $map = $this->parse($query, $params);
         $map = $this->parse($query, $params);
 
 
         if (empty($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);
             $this->saveQuery($query, \microtime(true) - $start);
 
 
-            return $result;
+            if (! $stmt instanceof PDOStatement) {
+                return false;
+            }
+
+            return $this->dbStatement($stmt);
         }
         }
 
 
         $start       = \microtime(true);
         $start       = \microtime(true);
-        $stmt        = parent::prepare($query);
+        $stmt        = $this->pdo->prepare($query);
         $this->delta = \microtime(true) - $start;
         $this->delta = \microtime(true) - $start;
 
 
+        if (! $stmt instanceof PDOStatement) {
+            return false;
+        }
+
+        $stmt = $this->dbStatement($stmt);
+
         $stmt->setMap($map);
         $stmt->setMap($map);
 
 
         if ($stmt->execute($params)) {
         if ($stmt->execute($params)) {
@@ -373,7 +431,8 @@ class DB extends PDO
     public function beginTransaction(): bool
     public function beginTransaction(): bool
     {
     {
         $start  = \microtime(true);
         $start  = \microtime(true);
-        $result = parent::beginTransaction();
+        $result = $this->pdo->beginTransaction();
+
         $this->saveQuery('beginTransaction()', \microtime(true) - $start, false);
         $this->saveQuery('beginTransaction()', \microtime(true) - $start, false);
 
 
         return $result;
         return $result;
@@ -385,7 +444,8 @@ class DB extends PDO
     public function commit(): bool
     public function commit(): bool
     {
     {
         $start  = \microtime(true);
         $start  = \microtime(true);
-        $result = parent::commit();
+        $result = $this->pdo->commit();
+
         $this->saveQuery('commit()', \microtime(true) - $start, false);
         $this->saveQuery('commit()', \microtime(true) - $start, false);
 
 
         return $result;
         return $result;
@@ -397,9 +457,26 @@ class DB extends PDO
     public function rollback(): bool
     public function rollback(): bool
     {
     {
         $start  = \microtime(true);
         $start  = \microtime(true);
-        $result = parent::rollback();
+        $result = $this->pdo->rollback();
+
         $this->saveQuery('rollback()', \microtime(true) - $start, false);
         $this->saveQuery('rollback()', \microtime(true) - $start, false);
 
 
         return $result;
         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);
+    }
 }
 }