index/src/Data/DbTools.php

102 lines
3.3 KiB
PHP

<?php
// DbTools.php
// Created: 2021-05-02
// Updated: 2022-02-28
namespace Index\Data;
use InvalidArgumentException;
use Index\Type;
/**
* Common database actions.
*/
final class DbTools {
private const DB_PROTOS = [
'null' => NullDb\NullDbBackend::class,
'mariadb' => MariaDB\MariaDBBackend::class,
'mysql' => MariaDB\MariaDBBackend::class,
'sqlite' => SQLite\SQLiteBackend::class,
'sqlite3' => SQLite\SQLiteBackend::class,
];
public static function create(string $dsn): IDbConnection {
static $backends = [];
$uri = parse_url($dsn);
if($uri === false)
throw new InvalidArgumentException('$dsn is not a valid uri.');
$scheme = $uri['scheme'];
if(in_array($scheme, $backends))
$backend = $backends[$scheme];
else {
$backend = null;
if(array_key_exists($scheme, self::DB_PROTOS))
$name = self::DB_PROTOS[$scheme];
else
$name = str_replace('-', '\\', $scheme);
if(class_exists($name) && is_subclass_of($name, IDbBackend::class)) {
$backend = new $name;
$name = get_class($backend);
}
if($backend === null)
throw new DataException('No implementation is available for the specified scheme.');
if(!$backend->isAvailable())
throw new DataException('Requested database backend is not available, likely due to missing dependencies.');
$backends[$name] = $backend;
}
return $backend->createConnection(
$backend->parseDsn($uri)
);
}
/**
* Transaction wrapper.
*
* Takes a database connection with transaction support and a callable that may return a boolean based on the success of the actions.
* If the callable returns nothing, nothing will happen.
* If the callable returns true, commit will be called.
* If the callable returns false, rollback will be called.
*
* @param IDbTransactions $connection A database connection with transaction support.
* @param callable $callable A callable that handles the transaction, may return a bool.
*/
public static function transaction(IDbTransactions $connection, callable $callable): void {
$connection->beginTransaction();
$result = $callable($connection) ?? null;
if(is_bool($result)) {
if($result)
$connection->commit();
else
$connection->rollback();
}
}
/**
* Detects the DbType of the passed argument. Should be used for DbType::AUTO.
*
* @param mixed $value A value of unknown type.
* @return int DbType of the value passed in the argument.
*/
public static function detectType(mixed $value): int {
if(is_null($value))
return DbType::NULL;
if(is_float($value))
return DbType::FLOAT;
if(is_int($value))
return DbType::INTEGER;
// ┌ should probably also check for Stringable, length should also be taken into consideration
// ↓ though maybe with that it's better to assume that when an object is passed it'll always be Massive
if(is_string($value))
return DbType::STRING;
return DbType::BLOB;
}
}