Type conversion for #9

Added BOOLEAN to SQLite types.
Expanded DBStatement (started).
The fecth() method for INTEGER and BOOLEAN fields returns integer/float(?) and bool data.
This commit is contained in:
Visman 2021-12-15 17:16:54 +07:00
parent 59dfe8004c
commit 1077d2c6b6
4 changed files with 170 additions and 4 deletions

View file

@ -66,8 +66,11 @@ class DB extends PDO
throw new PDOException("Driver isn't found for '$type'");
}
$statement = DBStatement::class;
if ('sqlite' === $type) {
$dsn = \str_replace('!PATH!', \realpath(__DIR__ . '/../config/db') . '/', $dsn);
$statement = 'ForkBB\\Core\\DB\\' . \ucfirst($type) . 'Statement';
}
$this->dbType = $type;
@ -78,7 +81,7 @@ class DB extends PDO
self::ATTR_EMULATE_PREPARES => false,
self::ATTR_STRINGIFY_FETCHES => false,
self::ATTR_ERRMODE => self::ERRMODE_EXCEPTION,
self::ATTR_STATEMENT_CLASS => [DBStatement::class, [$this]],
self::ATTR_STATEMENT_CLASS => [$statement, [$this]],
];
$start = \microtime(true);

View file

@ -0,0 +1,70 @@
<?php
/**
* This file is part of the ForkBB <https://github.com/forkbb>.
*
* @copyright (c) Visman <mio.visman@yandex.ru, https://github.com/MioVisman>
* @license The MIT License (MIT)
*/
declare(strict_types=1);
namespace ForkBB\Core\DB;
use ForkBB\Core\DBStatement;
use PDO;
use PDOStatement;
use PDOException;
use RuntimeException;
use ReturnTypeWillChange;
abstract class AbstractStatement extends DBStatement
{
const BOOLEAN = 'b';
const FLOAT = 'f';
const INTEGER = 'i';
const STRING = 's';
/**
* Типы столбцов полученные через getColumnMeta()
* @var array
*/
protected $columnsType;
abstract public function getColumnsType(): array;
abstract protected function convToBoolean(/* mixed */ $value): bool;
#[ReturnTypeWillChange]
public function fetch(/* int */ $mode = 0 /* PDO::FETCH_DEFAULT */, /* int */ $cursorOrientation = PDO::FETCH_ORI_NEXT, /* int */ $cursorOffset = 0) /* : mixed */
{
$data = parent::fetch($mode, $cursorOrientation, $cursorOffset);
if (! \is_array($data)) {
return $data;
}
$types = $this->getColumnsType();
foreach ($data as $key => &$value) {
if (isset($types[$key])) {
switch ($types[$key]) {
case self::INTEGER:
$value += 0; // If the string is not a number, then Warning/Notice
// It can return not an integer, but a float.
break;
case self::BOOLEAN:
$value = $this->convToBoolean($value);
break;
case self::FLOAT:
case self::STRING:
break;
default:
throw new RuntimeException("Unknown field type: '{$types[$key]}'");
}
}
}
unset($value);
return $data;
}
}

View file

@ -37,7 +37,8 @@ class Sqlite
'%^.*?(?:CHAR|CLOB|TEXT).*$%i' => 'TEXT',
'%^.*?BLOB.*$%i' => 'BLOB',
'%^.*?(?:REAL|FLOA|DOUB).*$%i' => 'REAL',
'%^.*?(?:NUMERIC|DECIMAL|BOOL|DATE).*$%i' => 'NUMERIC',
'%^.*?(?:NUMERIC|DECIMAL).*$%i' => 'NUMERIC',
'%^.*?BOOL.*$%i' => 'BOOLEAN', // ???? не соответствует SQLite
'%^SERIAL$%i' => 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
];
@ -46,9 +47,10 @@ class Sqlite
* @var array
*/
protected $types = [
'boolean' => 'b',
'integer' => 'i',
'real' => 'i',
'numeric' => 'i',
'real' => 'f',
'numeric' => 'f',
];
public function __construct(DB $db, string $prefix)

View file

@ -0,0 +1,91 @@
<?php
/**
* This file is part of the ForkBB <https://github.com/forkbb>.
*
* @copyright (c) Visman <mio.visman@yandex.ru, https://github.com/MioVisman>
* @license The MIT License (MIT)
*/
declare(strict_types=1);
namespace ForkBB\Core\DB;
use ForkBB\Core\DB\AbstractStatement;
use PDO;
use PDOStatement;
use PDOException;
use ReturnTypeWillChange;
class SqliteStatement extends AbstractStatement
{
/**
* https://github.com/php/php-src/blob/master/ext/pdo_sqlite/sqlite_statement.c
*
* SQLite:
* native_type:
* null - для значения NULL, а не типа столбца
* integer - это INTEGER, NUMERIC(?), BOOLEAN // BOOLEAN тут как-то не к месту, его бы в отдельный тип
* string - это TEXT
* double - это REAL, NUMERIC(?) // NUMERIC может быть и double, и integer
* sqlite:decl_type:
* INTEGER
* TEXT
* REAL
* NUMERIC
* BOOLEAN
* ... (это те типы, которые прописаны в CREATE TABLE и полученные после перекодировки из {driver}::bTypeRepl)
*/
/**
* @var array
*/
protected $nativeTypeRepl = [
'integer' => self::INTEGER,
'double' => self::FLOAT,
];
public function getColumnsType(): array
{
if ($this->columnsType) {
return $this->columnsType;
}
$this->columnsType = [];
$count = $this->columnCount();
$i = 0;
// $dbType = $this->db->getType();
for ($i = 0; $i < $count; $i++) {
$meta = $this->getColumnMeta($i);
$type = null;
// $declType = $meta[$dbType . ':decl_type'] ?? null;
$declType = $meta['sqlite:decl_type'] ?? null;
if (null === $declType) {
$type = $this->nativeTypeRepl[$meta['native_type']] ?? null;
} elseif (\preg_match('%INT%i', $declType)) {
$type = self::INTEGER;
} elseif (\preg_match('%BOOL%i', $declType)) {
$type = self::BOOLEAN;
// } elseif (\preg_match('%REAL|FLOA|DOUB|NUMERIC|DECIMAL%i', $declType)) {
// $type = self::FLOAT;
}
if ($type) {
$this->columnsType[$i] = $type;
if (isset($meta['name'])) { // ????? проверка на тип содержимого? только строки, не числа?
$this->columnsType[$meta['name']] = $type;
}
}
}
return $this->columnsType;
}
protected function convToBoolean(/* mixed */ $value): bool
{
return (bool) $value;
}
}