Compare commits

..

No commits in common. "f137dfaab5851ee3b8d7367725885321609d1cdd" and "bf7977f511e3ee7ee7ce33e6945d0e78b042e722" have entirely different histories.

33 changed files with 333 additions and 308 deletions

View file

@ -1 +1 @@
0.2410.191515
0.2410.182054

View file

@ -1,7 +1,7 @@
<?php
// ArrayCacheProvider.php
// Created: 2024-04-10
// Updated: 2024-10-19
// Updated: 2024-10-02
namespace Index\Cache\ArrayCache;
@ -74,4 +74,6 @@ class ArrayCacheProvider implements CacheProvider {
public function decrement(string $key, int $amount = 1): int {
return $this->increment($key, $amount * -1);
}
public function close(): void {}
}

View file

@ -1,14 +1,16 @@
<?php
// CacheProvider.php
// Created: 2024-04-10
// Updated: 2024-10-19
// Updated: 2024-10-02
namespace Index\Cache;
use Index\Closeable;
/**
* Represents a cache provider.
*/
interface CacheProvider {
interface CacheProvider extends Closeable {
/**
* Retrieve an item from the cache.
*

View file

@ -1,7 +1,7 @@
<?php
// MemcachedProvider.php
// Created: 2024-04-10
// Updated: 2024-10-19
// Updated: 2024-10-02
namespace Index\Cache\Memcached;
@ -19,4 +19,9 @@ abstract class MemcachedProvider implements CacheProvider {
public abstract function touch(string $key, int $ttl = 0): void;
public abstract function increment(string $key, int $amount = 1): int;
public abstract function decrement(string $key, int $amount = 1): int;
public abstract function close(): void;
public function __destruct() {
$this->close();
}
}

View file

@ -1,7 +1,7 @@
<?php
// MemcachedProviderLegacy.php
// Created: 2024-04-10
// Updated: 2024-10-19
// Updated: 2024-10-02
namespace Index\Cache\Memcached;
@ -91,7 +91,7 @@ class MemcachedProviderLegacy extends MemcachedProvider {
return $result;
}
public function __destruct() {
public function close(): void {
if(!$this->persistent)
$this->memcache->close();
}

View file

@ -1,7 +1,7 @@
<?php
// MemcachedProviderModern.php
// Created: 2024-04-10
// Updated: 2024-10-19
// Updated: 2024-10-02
namespace Index\Cache\Memcached;
@ -111,7 +111,7 @@ class MemcachedProviderModern extends MemcachedProvider {
return $result;
}
public function __destruct() {
public function close(): void {
if(!$this->memcached->isPersistent())
$this->memcached->quit();
}

View file

@ -1,7 +1,7 @@
<?php
// ValkeyProvider.php
// Created: 2024-04-10
// Updated: 2024-10-19
// Updated: 2024-10-02
namespace Index\Cache\Valkey;
@ -72,8 +72,12 @@ class ValkeyProvider implements CacheProvider {
return is_int($result) ? $result : 0;
}
public function __destruct() {
public function close(): void {
if(!$this->persist)
$this->redis->close();
}
public function __destruct() {
$this->close();
}
}

30
src/Closeable.php Normal file
View file

@ -0,0 +1,30 @@
<?php
// Closeable.php
// Created: 2021-04-30
// Updated: 2024-10-02
namespace Index;
/**
* Provides an interface for releasing unmanaged resources.
*
* If Closeable is implemented __destruct() should also be added to the class and should call close in it:
*
* <code>
* public function close(): void {
* fclose($this->resource);
* }
*
* public function __destruct() {
* $this->close();
* }
* </code>
*
* However if close() is only implemented because a parent interface requires it, the __destruct() implementation may be omitted.
*/
interface Closeable {
/**
* Free, release or reset unmanaged resources.
*/
function close(): void;
}

View file

@ -1,16 +1,16 @@
<?php
// DbConnection.php
// Created: 2021-04-30
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db;
use RuntimeException;
use Index\Closeable;
/**
* Represents a connection to a database service.
*/
interface DbConnection {
interface DbConnection extends Closeable {
/**
* Returns the ID of the last inserted row.
*
@ -53,11 +53,4 @@ interface DbConnection {
* @return int|string Number of rows affected by the query.
*/
function execute(string $query): int|string;
/**
* Begins a transaction.
*
* @throws RuntimeException If beginning the transaction failed.
*/
function beginTransaction(): DbTransaction;
}

View file

@ -1,14 +1,16 @@
<?php
// DbResult.php
// Created: 2021-05-02
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db;
use Index\Closeable;
/**
* Represents a database result set.
*/
interface DbResult {
interface DbResult extends Closeable {
/**
* Fetches the next result set.
*

View file

@ -1,17 +1,18 @@
<?php
// DbStatement.php
// Created: 2021-05-02
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db;
use InvalidArgumentException;
use RuntimeException;
use Index\Closeable;
/**
* Represents a prepared database statement.
*/
interface DbStatement {
interface DbStatement extends Closeable {
/**
* Returns how many parameters there are.
*
@ -24,7 +25,7 @@ interface DbStatement {
*
* @param int $ordinal Index of the target parameter.
* @param mixed $value Value to assign to the parameter.
* @param int $type Type of the value, if left to DbType::AUTO DbType::detect will be used on $value.
* @param int $type Type of the value, if left to DbType::AUTO DbTools::detectType will be used on $value.
* @throws InvalidArgumentException If $ordinal exceeds bounds.
*/
function addParameter(int $ordinal, mixed $value, int $type = DbType::AUTO): void;
@ -36,7 +37,7 @@ interface DbStatement {
* Overwriting lower ordinals than the current cursor should be fine, but your mileage may vary.
*
* @param mixed $value Value to assign to the parameter.
* @param int $type Type of the value, if left to DbType::AUTO DbType::detect will be used on $value.
* @param int $type Type of the value, if left to DbType::AUTO DbTools::detectType will be used on $value.
* @throws RuntimeException If all parameters have already been specified.
*/
function nextParameter(mixed $value, int $type = DbType::AUTO): void;

View file

@ -1,7 +1,7 @@
<?php
// DbStatementCache.php
// Created: 2023-07-21
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db;
@ -54,6 +54,8 @@ class DbStatementCache {
* Closes all statement instances and resets the cache.
*/
public function clear(): void {
foreach($this->stmts as $stmt)
$stmt->close();
$this->stmts = [];
}
}

View file

@ -1,7 +1,7 @@
<?php
// DbTools.php
// Created: 2021-05-02
// Updated: 2024-10-19
// Updated: 2024-10-16
namespace Index\Db;
@ -11,6 +11,46 @@ use Countable;
* Common database actions.
*/
final class DbTools {
/**
* 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 DbTransactions $connection A database connection with transaction support.
* @param callable $callable A callable that handles the transaction, may return a bool.
*/
public static function transaction(DbTransactions $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;
if(is_resource($value))
return DbType::BLOB;
return DbType::STRING;
}
/**
* Constructs a partial query for prepared statements of lists.
*

View file

@ -1,46 +0,0 @@
<?php
// DbTransaction.php
// Created: 2024-10-19
// Updated: 2024-10-19
namespace Index\Db;
use RuntimeException;
/**
* Represents a transaction to be performed on the database.
*/
interface DbTransaction {
/**
* Commits the actions done during a transaction and ends the transaction.
* A new transaction will be started if auto-commit is disabled.
*
* @throws RuntimeException If the commit failed.
*/
function commit(): void;
/**
* Creates a save point in the transaction that can be rolled back to.
*
* @param string $name Name for the save point.
* @throws RuntimeException If save point creation failed.
*/
function save(string $name): void;
/**
* Releases a specified save point.
*
* @param string $name Name of the save point.
* @throws RuntimeException If save points are not supported by this implementation.
*/
function release(string $name): void;
/**
* Rolls back to the state before a transaction start or to a specified save point.
*
* @param ?string $name Name of the save point, null for the entire transaction.
* @throws RuntimeException If rollback failed.
* @throws RuntimeException If save points are not supported by this implementation and $name was non-null.
*/
function rollback(?string $name = null): void;
}

57
src/Db/DbTransactions.php Normal file
View file

@ -0,0 +1,57 @@
<?php
// DbTransactions.php
// Created: 2021-05-02
// Updated: 2024-10-04
namespace Index\Db;
/**
* Indicates supports for transactions in a database connection.
*/
interface DbTransactions extends DbConnection {
/**
* Sets whether changes should be applied immediately or whether commit should always be called first.
*
* @param bool $state true if things should automatically be committed, false if not.
*/
function setAutoCommit(bool $state): void;
/**
* Enters a transaction.
*
* @throws \RuntimeException If the creation of the transaction failed.
*/
function beginTransaction(): void;
/**
* Commits the actions done during a transaction and ends the transaction.
* A new transaction will be started if auto-commit is disabled.
*
* @throws \RuntimeException If the commit failed.
*/
function commit(): void;
/**
* Rolls back to the state before a transaction start or to a specified save point.
*
* @param ?string $name Name of the save point, null for the entire transaction.
* @throws \RuntimeException If rollback failed.
*/
function rollback(?string $name = null): void;
/**
* Creates a save point in the transaction that can be rolled back to.
*
* @param string $name Name for the save point.
* @throws \RuntimeException If save point creation failed.
*/
function savePoint(string $name): void;
/**
* Releases a save point.
*
* @param string $name Name of the save point.
* @throws \RuntimeException If releasing the save point failed.
*/
function releaseSavePoint(string $name): void;
}

View file

@ -1,7 +1,7 @@
<?php
// DbType.php
// Created: 2021-05-02
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db;
@ -10,7 +10,7 @@ namespace Index\Db;
*/
final class DbType {
/**
* Automatically detect the type. Should be used in combination with DbType::detect.
* Automatically detect the type. Should be used in combination with DbTools::detectType.
*
* @var int
*/
@ -50,22 +50,4 @@ final class DbType {
* @var int
*/
public const BLOB = 5;
/**
* 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 detect(mixed $value): int {
if(is_null($value))
return self::NULL;
if(is_float($value))
return self::FLOAT;
if(is_int($value))
return self::INTEGER;
if(is_resource($value))
return self::BLOB;
return self::STRING;
}
}

View file

@ -1,7 +1,7 @@
<?php
// MariaDbConnection.php
// Created: 2021-04-30
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db\MariaDb;
@ -10,12 +10,12 @@ use mysqli_sql_exception;
use stdClass;
use InvalidArgumentException;
use RuntimeException;
use Index\Db\{DbConnection,DbTransaction};
use Index\Db\{DbConnection,DbTransactions};
/**
* Represents a connection with a MariaDB or MySQL database server.
*/
class MariaDbConnection implements DbConnection {
class MariaDbConnection implements DbConnection, DbTransactions {
/**
* Refresh grant tables.
*
@ -144,8 +144,6 @@ class MariaDbConnection implements DbConnection {
} catch(mysqli_sql_exception $ex) {
throw new RuntimeException($ex->getMessage(), $ex->getCode(), $ex);
}
$this->connection->autocommit(true);
}
public function getAffectedRows(): int|string {
@ -191,7 +189,7 @@ class MariaDbConnection implements DbConnection {
*/
public function switchUser(string $userName, string $password, string|null $database = null): void {
if(!$this->connection->change_user($userName, $password, $database === null ? null : $database))
$this->throwLastError();
throw new RuntimeException($this->getLastErrorString(), $this->getLastErrorCode());
}
/**
@ -202,7 +200,7 @@ class MariaDbConnection implements DbConnection {
*/
public function switchDatabase(string $database): void {
if(!$this->connection->select_db($database))
$this->throwLastError();
throw new RuntimeException($this->getLastErrorString(), $this->getLastErrorCode());
}
/**
@ -232,15 +230,6 @@ class MariaDbConnection implements DbConnection {
return $this->connection->error;
}
/**
* Throws the last error as a RuntimeException.
*
* @throws RuntimeException Based on the last error code and string.
*/
public function throwLastError(): never {
throw new RuntimeException((string)$this->getLastErrorString(), $this->getLastErrorCode());
}
/**
* Gets the current SQL State of the connection.
*
@ -313,11 +302,33 @@ class MariaDbConnection implements DbConnection {
$this->connection->refresh($flags);
}
public function beginTransaction(): DbTransaction {
if(!$this->connection->begin_transaction())
$this->throwLastError();
public function setAutoCommit(bool $state): void {
$this->connection->autocommit($state);
}
return new MariaDbTransaction($this, $this->connection);
public function beginTransaction(): void {
if(!$this->connection->begin_transaction())
throw new RuntimeException((string)$this->getLastErrorString(), $this->getLastErrorCode());
}
public function commit(): void {
if(!$this->connection->commit())
throw new RuntimeException((string)$this->getLastErrorString(), $this->getLastErrorCode());
}
public function rollback(?string $name = null): void {
if(!$this->connection->rollback(0, $name))
throw new RuntimeException((string)$this->getLastErrorString(), $this->getLastErrorCode());
}
public function savePoint(string $name): void {
if(!$this->connection->savepoint($name))
throw new RuntimeException((string)$this->getLastErrorString(), $this->getLastErrorCode());
}
public function releaseSavePoint(string $name): void {
if(!$this->connection->release_savepoint($name))
throw new RuntimeException((string)$this->getLastErrorString(), $this->getLastErrorCode());
}
/**
@ -349,7 +360,7 @@ class MariaDbConnection implements DbConnection {
throw new RuntimeException($ex->getMessage(), $ex->getCode(), $ex);
}
if($statement === false)
$this->throwLastError();
throw new RuntimeException($this->getLastErrorString(), $this->getLastErrorCode());
return new MariaDbStatement($statement);
}
@ -364,7 +375,7 @@ class MariaDbConnection implements DbConnection {
}
if($result === false)
$this->throwLastError();
throw new RuntimeException($this->getLastErrorString(), $this->getLastErrorCode());
// the mysql library is very adorable
if($result === true)
@ -377,7 +388,7 @@ class MariaDbConnection implements DbConnection {
public function execute(string $query): int|string {
try {
if(!$this->connection->real_query($query))
$this->throwLastError();
throw new RuntimeException($this->getLastErrorString(), $this->getLastErrorCode());
} catch(mysqli_sql_exception $ex) {
throw new RuntimeException($ex->getMessage(), $ex->getCode(), $ex);
}
@ -387,9 +398,16 @@ class MariaDbConnection implements DbConnection {
/**
* Closes the connection and associated resources.
*/
public function __destruct() {
public function close(): void {
try {
$this->connection->close();
} catch(\Error $ex) {}
}
/**
* Closes the connection and associated resources.
*/
public function __destruct() {
$this->close();
}
}

View file

@ -1,11 +1,11 @@
<?php
// MariaDbParameter.php
// Created: 2021-05-02
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db\MariaDb;
use Index\Db\DbType;
use Index\Db\{DbTools,DbType};
/**
* Represents a bound parameter.
@ -25,7 +25,7 @@ class MariaDbParameter {
private mixed $value
) {
if($type == DbType::AUTO)
$type = DbType::detect($value);
$type = DbTools::detectType($value);
if($type === DbType::NULL)
$value = null;
}

View file

@ -1,7 +1,7 @@
<?php
// MariaDbResult.php
// Created: 2021-05-02
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db\MariaDb;
@ -66,4 +66,10 @@ abstract class MariaDbResult implements DbResult {
public function getValue(int|string $index): mixed {
return $this->currentRow[$index] ?? null;
}
abstract function close(): void;
public function __destruct() {
$this->close();
}
}

View file

@ -1,7 +1,7 @@
<?php
// MariaDbResultLib.php
// Created: 2021-05-02
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db\MariaDb;
@ -54,7 +54,7 @@ class MariaDbResultLib extends MariaDbResult {
return true;
}
public function __destruct() {
public function close(): void {
try {
$this->result->free_result();
} catch(\Error $ex) {}

View file

@ -1,7 +1,7 @@
<?php
// MariaDbResultNative.php
// Created: 2021-05-02
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db\MariaDb;
@ -36,7 +36,7 @@ class MariaDbResultNative extends MariaDbResult {
return true;
}
public function __destruct() {
public function close(): void {
try {
$this->result->close();
} catch(\Error $ex) {}

View file

@ -1,7 +1,7 @@
<?php
// MariaDbStatement.php
// Created: 2021-05-02
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db\MariaDb;
@ -159,9 +159,13 @@ class MariaDbStatement implements DbStatement {
throw new RuntimeException($this->getLastErrorString(), $this->getLastErrorCode());
}
public function __destruct() {
public function close(): void {
$this->statement->close();
}
public function __destruct() {
$this->close();
}
}
MariaDbStatement::construct();

View file

@ -1,43 +0,0 @@
<?php
// MariaDbTransaction.php
// Created: 2024-10-19
// Updated: 2024-10-19
namespace Index\Db\MariaDb;
use mysqli;
use Index\Db\DbTransaction;
/**
* Represents a MariaDB transaction.
*/
class MariaDbTransaction implements DbTransaction {
/**
* @param MariaDbConnection $conn Underlying connection
* @param mysqli $connRaw Underlying connection (off vocal)
*/
public function __construct(
private MariaDbConnection $conn,
private mysqli $connRaw
) {}
public function commit(): void {
if(!$this->connRaw->commit())
$this->conn->throwLastError();
}
public function save(string $name): void {
if(!$this->connRaw->savepoint($name))
$this->conn->throwLastError();
}
public function release(string $name): void {
if(!$this->connRaw->release_savepoint($name))
$this->conn->throwLastError();
}
public function rollback(?string $name = null): void {
if(!$this->connRaw->rollback(0, $name))
$this->conn->throwLastError();
}
}

View file

@ -1,11 +1,11 @@
<?php
// NullDbConnection.php
// Created: 2021-05-02
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db\NullDb;
use Index\Db\{DbConnection,DbResult,DbStatement,DbTransaction};
use Index\Db\{DbConnection,DbStatement,DbResult};
/**
* Represents a dummy database connection.
@ -31,7 +31,5 @@ class NullDbConnection implements DbConnection {
return 0;
}
public function beginTransaction(): DbTransaction {
return new NullDbTransaction;
}
public function close(): void {}
}

View file

@ -1,7 +1,7 @@
<?php
// NullDbResult.php
// Created: 2021-05-02
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db\NullDb;
@ -58,4 +58,6 @@ class NullDbResult implements DbResult {
public function getIterator(callable $construct): DbResultIterator {
return new DbResultIterator($this, $construct);
}
public function close(): void {}
}

View file

@ -1,7 +1,7 @@
<?php
// NullDbStatement.php
// Created: 2021-05-02
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db\NullDb;
@ -32,4 +32,6 @@ class NullDbStatement implements DbStatement {
}
public function reset(): void {}
public function close(): void {}
}

View file

@ -1,20 +0,0 @@
<?php
// NullDbTransaction.php
// Created: 2024-10-19
// Updated: 2024-10-19
namespace Index\Db\NullDb;
use Index\Db\{DbConnection,DbTransaction};
/**
* Represents a dummy database transaction.
*/
class NullDbTransaction implements DbTransaction {
public function commit(): void {}
public function save(string $name): void {}
public function release(string $name): void {}
public function rollback(?string $name = null): void {}
}

View file

@ -1,18 +1,18 @@
<?php
// SqliteConnection.php
// Created: 2021-05-02
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db\Sqlite;
use RuntimeException;
use SQLite3;
use Index\Db\{DbConnection,DbResult,DbStatement,DbTransaction};
use Index\Db\{DbConnection,DbTransactions,DbStatement,DbResult};
/**
* Represents a client for an SQLite database.
*/
class SqliteConnection implements DbConnection {
class SqliteConnection implements DbConnection, DbTransactions {
/**
* CREATE INDEX authorizer.
*
@ -352,6 +352,7 @@ class SqliteConnection implements DbConnection {
public const IGNORE = SQLite3::IGNORE;
private SQLite3 $connection;
private bool $autoCommit = true;
/**
* Creates a new SQLite client.
@ -455,15 +456,6 @@ class SqliteConnection implements DbConnection {
return $this->connection->lastErrorCode();
}
/**
* Throws the last error as a RuntimeException.
*
* @throws RuntimeException Based on the last error code and string.
*/
public function throwLastError(): never {
throw new RuntimeException((string)$this->getLastErrorString(), $this->getLastErrorCode());
}
/**
* Opens a BLOB field as a resource.
*
@ -476,7 +468,7 @@ class SqliteConnection implements DbConnection {
public function getBlobStream(string $table, string $column, int $rowId): mixed {
$handle = $this->connection->openBlob($table, $column, $rowId);
if(!is_resource($handle))
$this->throwLastError();
throw new RuntimeException((string)$this->getLastErrorString(), $this->getLastErrorCode());
return $handle;
}
@ -488,45 +480,73 @@ class SqliteConnection implements DbConnection {
public function prepare(string $query): DbStatement {
$statement = $this->connection->prepare($query);
if($statement === false)
$this->throwLastError();
throw new RuntimeException((string)$this->getLastErrorString(), $this->getLastErrorCode());
return new SqliteStatement($this, $statement);
}
public function query(string $query): DbResult {
$result = $this->connection->query($query);
if($result === false)
$this->throwLastError();
throw new RuntimeException($this->getLastErrorString(), $this->getLastErrorCode());
return new SqliteResult($result);
}
/**
* Tries to execute a query but throws an exception if it fails.
*
* @param string $query Command to execute.
* @throws RuntimeException If the command failed to execute.
*/
public function executeThrow(string $query): void {
if(!$this->connection->exec($query))
$this->throwLastError();
}
public function execute(string $query): int|string {
if(!$this->connection->exec($query))
$this->throwLastError();
throw new RuntimeException($this->getLastErrorString(), $this->getLastErrorCode());
return $this->connection->changes();
}
public function beginTransaction(): DbTransaction {
if(!$this->connection->exec('BEGIN;'))
$this->throwLastError();
public function setAutoCommit(bool $state): void {
// there's not really a completely reliable way to set a persistent auto commit disable state
if($state === $this->autoCommit)
return;
$this->autoCommit = $state;
return new SqliteTransaction($this);
if($state) {
$this->commit();
} else {
$this->beginTransaction();
}
}
public function __destruct() {
public function beginTransaction(): void {
if(!$this->connection->exec('BEGIN;'))
throw new RuntimeException((string)$this->getLastErrorString(), $this->getLastErrorCode());
}
public function commit(): void {
if(!$this->connection->exec('COMMIT;'))
throw new RuntimeException((string)$this->getLastErrorString(), $this->getLastErrorCode());
if(!$this->autoCommit)
$this->beginTransaction();
}
public function rollback(?string $name = null): void {
if(!$this->connection->exec(empty($name) ? 'ROLLBACK;' : "ROLLBACK TO {$name};"))
throw new RuntimeException((string)$this->getLastErrorString(), $this->getLastErrorCode());
if(!$this->autoCommit)
$this->beginTransaction();
}
public function savePoint(string $name): void {
if(!$this->connection->exec("SAVEPOINT {$name};"))
throw new RuntimeException((string)$this->getLastErrorString(), $this->getLastErrorCode());
}
public function releaseSavePoint(string $name): void {
if(!$this->connection->exec("RELEASE SAVEPOINT {$name};"))
throw new RuntimeException((string)$this->getLastErrorString(), $this->getLastErrorCode());
}
public function close(): void {
$this->connection->close();
}
/**
* Closes the connection and associated resources.
*/
public function __destruct() {
$this->close();
}
}

View file

@ -1,7 +1,7 @@
<?php
// SqliteResult.php
// Created: 2021-05-02
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db\Sqlite;
@ -52,4 +52,6 @@ class SqliteResult implements DbResult {
public function getValue(int|string $index): mixed {
return $this->currentRow[$index] ?? null;
}
public function close(): void {}
}

View file

@ -1,7 +1,7 @@
<?php
// SqliteStatement.php
// Created: 2021-05-02
// Updated: 2024-10-19
// Updated: 2024-10-04
namespace Index\Db\Sqlite;
@ -9,7 +9,7 @@ use SQLite3Result;
use SQLite3Stmt;
use InvalidArgumentException;
use RuntimeException;
use Index\Db\{DbType,DbStatement,DbResult};
use Index\Db\{DbTools,DbType,DbStatement,DbResult};
/**
* Represents a prepared SQLite SQL statement.
@ -47,7 +47,7 @@ class SqliteStatement implements DbStatement {
private function bindParameter(int $ordinal, mixed $value, int $type): void {
if($type === DbType::AUTO)
$type = DbType::detect($value);
$type = DbTools::detectType($value);
if($type === DbType::NULL)
$value = null;
@ -99,4 +99,6 @@ class SqliteStatement implements DbStatement {
if(!$this->statement->reset())
throw new RuntimeException((string)$this->connection->getLastErrorString(), $this->connection->getLastErrorCode());
}
public function close(): void {}
}

View file

@ -1,41 +0,0 @@
<?php
// SqliteTransaction.php
// Created: 2024-10-19
// Updated: 2024-10-19
namespace Index\Db\Sqlite;
use SQLite3;
use Index\Db\DbTransaction;
/**
* Represents an SQLite transaction.
*/
class SqliteTransaction implements DbTransaction {
/**
* @param SqliteConnection $conn Underlying connection.
*/
public function __construct(
private SqliteConnection $conn
) {}
public function commit(): void {
$this->conn->executeThrow('COMMIT;');
}
public function save(string $name): void {
$this->conn->executeThrow(sprintf('SAVEPOINT %s;', SQLite3::escapeString($name)));
}
public function release(string $name): void {
$this->conn->executeThrow(sprintf('RELEASE SAVEPOINT %s;', SQLite3::escapeString($name)));
}
public function rollback(?string $name = null): void {
$this->conn->executeThrow(
$name === null
? 'ROLLBACK;'
: sprintf('ROLLBACK TO SAVEPOINT %s;', SQLite3::escapeString($name))
);
}
}

30
tests/DbToolsTest.php Normal file
View file

@ -0,0 +1,30 @@
<?php
// DbToolsTest.php
// Created: 2021-04-28
// Updated: 2024-10-16
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\CoversClass;
use Index\Db\{DbTools,DbType};
#[CoversClass(DbTools::class)]
#[CoversClass(DbType::class)]
final class DbToolsTest extends TestCase {
public function testDetectType(): void {
$this->assertEquals(DbType::NULL, DbTools::detectType(null));
$this->assertEquals(DbType::INTEGER, DbTools::detectType(12345));
$this->assertEquals(DbType::FLOAT, DbTools::detectType(123.45));
$this->assertEquals(DbType::STRING, DbTools::detectType('This is a string.'));
$blob = fopen('php://memory', 'r+b');
if($blob === false)
throw new RuntimeException('failed to fopen a memory stream');
fwrite($blob, 'This is a string inside a memory stream.');
fseek($blob, 0);
$this->assertEquals(DbType::BLOB, DbTools::detectType($blob));
}
}

View file

@ -1,29 +0,0 @@
<?php
// DbTypeTest.php
// Created: 2021-04-28
// Updated: 2024-10-19
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\CoversClass;
use Index\Db\DbType;
#[CoversClass(DbType::class)]
final class DbTypeTest extends TestCase {
public function testDetectType(): void {
$this->assertEquals(DbType::NULL, DbType::detect(null));
$this->assertEquals(DbType::INTEGER, DbType::detect(12345));
$this->assertEquals(DbType::FLOAT, DbType::detect(123.45));
$this->assertEquals(DbType::STRING, DbType::detect('This is a string.'));
$blob = fopen('php://memory', 'r+b');
if($blob === false)
throw new RuntimeException('failed to fopen a memory stream');
fwrite($blob, 'This is a string inside a memory stream.');
fseek($blob, 0);
$this->assertEquals(DbType::BLOB, DbType::detect($blob));
}
}