ajout sqlite

This commit is contained in:
Jephté Clain 2024-04-24 23:16:55 +04:00
parent 3550730a9f
commit 3bd1031dda
5 changed files with 311 additions and 0 deletions

178
src/db/sqlite/Sqlite.php Normal file
View File

@ -0,0 +1,178 @@
<?php
namespace nur\sery\db\sqlite;
use nur\b\IllegalAccessException;
use nur\sery\cl;
use SQLite3;
use SQLite3Result;
use SQLite3Stmt;
/**
* Class Sqlite: frontend vers une base de données sqlite3
*/
class Sqlite {
static function config_enableExceptions(self $sqlite) {
$sqlite->db->enableExceptions(true);
}
static function verifix_query(&$query, ?array &$params=null): void {
if (is_array($query)) {
throw IllegalAccessException::not_implemented(); #XXX
} elseif (!is_string($query)) {
$query = strval($query);
}
}
const CONFIG = [
[self::class, "config_enableExceptions"],
];
const MIGRATE = null;
const SCHEMA = [
"file" => ["string", ""],
"flags" => ["int", SQLITE3_OPEN_READWRITE + SQLITE3_OPEN_CREATE],
"encryption_key" => ["string", ""],
"config" => ["?array|callable"],
"migrate" => ["?array|string|callable"],
"auto_open" => ["bool", true],
];
function __construct(?string $file=null, ?array $params=null) {
if ($file !== null) $params["file"] = $file;
##schéma
$default_file = self::SCHEMA["file"][1];
$this->file = strval($params["file"] ?? $default_file);
#
$default_flags = self::SCHEMA["flags"][1];
$this->flags = intval($params["flags"] ?? $default_flags);
#
$default_encryptionKey = self::SCHEMA["encryption_key"][1];
$this->encryptionKey = strval($params["encryption_key"] ?? $default_encryptionKey);
# configuration
$this->config = $params["config"] ?? static::CONFIG;
# migrations
$this->migration = $params["migrate"] ?? static::MIGRATE;
#
$default_autoOpen = self::SCHEMA["auto_open"][1];
if ($params["auto_open"] ?? $default_autoOpen) {
$this->open();
}
}
/** @var string */
protected $file;
/** @var int */
protected $flags;
/** @var string */
protected $encryptionKey;
/** @var array|string|callable */
protected $config;
/** @var array|string|callable */
protected $migration;
/** @var SQLite3 */
protected $db;
function open(): self {
if ($this->db === null) {
$this->db = new SQLite3($this->file, $this->flags, $this->encryptionKey);
SqliteConfig::with($this->config)->run($this);
SqliteMigration::with($this->migration)->run($this);
}
return $this;
}
function close(): void {
if ($this->db !== null) {
$this->db->close();
$this->db = null;
}
}
function exec($query, ?array $params=null): bool {
$this->open();
self::verifix_query($query, $params);
$db = $this->db;
if ($params === null) {
return $db->exec($query);
} else {
/** @var SQLite3Stmt $stmt */
$stmt = SqliteException::check($db, $db->prepare($query));
try {
foreach ($params as $param => $value) {
SqliteException::check($db, $stmt->bindValue($param, $value));
}
/** @var SQLite3Result $result */
$result = SqliteException::check($db, $stmt->execute());
return $result->finalize();
} finally {
$stmt->close();
}
}
}
function get($query, ?array $params=null, bool $entireRow=false) {
$this->open();
self::verifix_query($query, $params);
$db = $this->db;
if ($params === null) {
return $db->querySingle($query, $entireRow);
} else {
/** @var SQLite3Stmt $stmt */
$stmt = SqliteException::check($db, $db->prepare($query));
try {
foreach ($params as $param => $value) {
SqliteException::check($db, $stmt->bindValue($param, $value));
}
/** @var SQLite3Result $result */
$result = SqliteException::check($db, $stmt->execute());
try {
$row = $result->fetchArray(SQLITE3_ASSOC);
if ($row === false) return null;
elseif ($entireRow) return $row;
else return cl::first($row);
} finally {
$result->finalize();
}
} finally {
$stmt->close();
}
}
}
function all($query, ?array $params=null): iterable {
$this->open();
self::verifix_query($query, $params);
$db = $this->db;
if ($params === null) {
/** @var SQLite3Result $result */
$result = SqliteException::check($db, $db->query($query));
try {
while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) {
yield $row;
}
} finally {
$result->finalize();
}
} else {
/** @var SQLite3Stmt $stmt */
$stmt = SqliteException::check($db, $db->prepare($query));
try {
foreach ($params as $param => $value) {
SqliteException::check($db, $stmt->bindValue($param, $value));
}
/** @var SQLite3Result $result */
$result = SqliteException::check($db, $stmt->execute());
try {
while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) {
yield $row;
}
} finally {
$result->finalize();
}
} finally {
$stmt->close();
}
}
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace nur\sery\db\sqlite;
use nur\sery\php\func;
class SqliteConfig {
static function with($configs): SqliteConfig {
if ($configs instanceof self) return $configs;
return new static($configs);
}
const CONFIG = null;
function __construct($configs) {
if ($configs === null) $configs = static::CONFIG;
if ($configs === null) $configs = [];
elseif (is_string($configs)) $configs = [$configs];
elseif (is_callable($configs)) $configs = [$configs];
elseif (!is_array($configs)) $configs = [strval($configs)];
$this->configs = $configs;
}
/** @var array */
protected $configs;
function run(Sqlite $sqlite): void {
foreach ($this->configs as $key => $config) {
if (is_callable($config)) {
func::call($config, $sqlite, $key, $this);
} else {
$sqlite->exec($config);
}
}
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace nur\sery\db\sqlite;
use RuntimeException;
use SQLite3;
class SqliteException extends RuntimeException {
static final function check(SQLite3 $db, $value, bool $throw=true) {
if ($value !== false) return $value;
elseif (!$throw) return null;
else throw new static($db->lastErrorMsg(), $db->lastErrorCode());
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace nur\sery\db\sqlite;
use nur\sery\php\func;
class SqliteMigration {
static function with($migrations): SqliteMigration {
if ($migrations instanceof self) return $migrations;
return new static($migrations);
}
const MIGRATE = null;
function __construct($migrations) {
if ($migrations === null) $migrations = static::MIGRATE;
if ($migrations === null) $migrations = [];
elseif (is_string($migrations)) $migrations = [$migrations];
elseif (is_callable($migrations)) $migrations = [$migrations];
elseif (!is_array($migrations)) $migrations = [strval($migrations)];
$this->migrations = $migrations;
}
/** @var callable[]|string[] */
protected $migrations;
function run(Sqlite $sqlite): void {
$sqlite->exec("create table if not exists _migration(key varchar primary key, value varchar not null, done integer default 0)");
foreach ($this->migrations as $key => $migration) {
$exists = $sqlite->get("select 1 from _migration where key = :key and done = 1", [
"key" => $key,
]);
if (!$exists) {
$sqlite->exec("insert or replace into _migration(key, value, done) values(:key, :value, :done)", [
"key" => $key,
"value" => $migration,
"done" => 0,
]);
if (is_callable($migration)) {
func::call($migration, $sqlite, $key, $this);
} else {
$sqlite->exec($migration);
}
$sqlite->exec("update _migration set done = 1 where key = :key", [
"key" => $key,
]);
}
}
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace nur\sery\db\sqlite;
use PHPUnit\Framework\TestCase;
class SqliteTest extends TestCase {
const CREATE_PERSON = "create table person(nom varchar, prenom varchar, age integer)";
const INSERT_JEPHTE = "insert into person(nom, prenom, age) values ('clain', 'jephte', 50)";
const INSERT_JEAN = "insert into person(nom, prenom, age) values ('payet', 'jean', 32)";
function testMigration() {
$sqlite = new Sqlite(":memory:", [
"migrate" => [
self::CREATE_PERSON,
self::INSERT_JEPHTE,
],
]);
self::assertSame("clain", $sqlite->get("select nom, age from person"));
self::assertSame([
"nom" => "clain",
"age" => 50,
], $sqlite->get("select nom, age from person", null, true));
$sqlite->exec(self::INSERT_JEAN);
self::assertSame("payet", $sqlite->get("select nom, age from person where nom = 'payet'"));
self::assertSame([
"nom" => "payet",
"age" => 32,
], $sqlite->get("select nom, age from person where nom = 'payet'", null, true));
self::assertSame([
["key" => "0", "value" => self::CREATE_PERSON, "done" => 1],
["key" => "1", "value" => self::INSERT_JEPHTE, "done" => 1],
], iterator_to_array($sqlite->all("select key, value, done from _migration")));
}
}