241 lines
5.6 KiB
PHP
241 lines
5.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace mirzaev\csv;
|
|
|
|
// Files of the project
|
|
use mirzaev\csv\traits\file;
|
|
|
|
// Built-in libraries
|
|
use exception;
|
|
|
|
/**
|
|
* Database
|
|
*
|
|
* Comma-Separated Values by RFC 4180
|
|
*
|
|
* @see https://tools.ietf.org/html/rfc4180 RFC 4180
|
|
* @see https://en.wikipedia.org/wiki/Create,_read,_update_and_delete CRUD
|
|
*
|
|
* @package mirzaev\csv
|
|
*
|
|
* @var string FILE Path to the database file
|
|
* @var array $columns Database columns
|
|
*
|
|
* @method void __construct(array|null $columns) Constructor
|
|
* @method void create() Write to the database file
|
|
* @method array|null read(int $amount, int $offset, bool $backwards, ?callable $filter) Read from the database file
|
|
* @method void update() @todo create + tests
|
|
* @method void delete() @todo create + teste
|
|
* @method void __set(string $name, mixed $value) Write the parameter
|
|
* @method mized __get(string $name) Read the parameter
|
|
* @method void __unset(string $name) Delete the parameter
|
|
* @method bool __isset(string $name) Check for initializing the parameter
|
|
*
|
|
* @license http://www.wtfpl.net Do What The Fuck You Want To Public License
|
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
|
*/
|
|
|
|
class database
|
|
{
|
|
use file {
|
|
file::read as protected file;
|
|
}
|
|
|
|
/**
|
|
* File
|
|
*
|
|
* Path directories to the file will not be created automatically to avoid
|
|
* checking the existence of all directories on every read or write operation.
|
|
*
|
|
* @var string FILE Path to the database file
|
|
*/
|
|
public const string FILE = 'database.csv';
|
|
|
|
/**
|
|
* Columns
|
|
*
|
|
* This property is used instead of adding a check for the presence of the first row
|
|
* with the designation of the column names, as well as reading these columns,
|
|
* which would significantly slow down the library.
|
|
*
|
|
* @see https://www.php.net/manual/en/function.array-combine.php Used when creating a record instance
|
|
*
|
|
* @var array $columns Database columns
|
|
*/
|
|
public protected(set) array $columns;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param array|null $columns Columns
|
|
*
|
|
* @return void
|
|
*/
|
|
public function __construct(?array $columns = null)
|
|
{
|
|
// Initializing columns
|
|
if (isset($columns)) $this->columns = $columns;
|
|
}
|
|
|
|
/**
|
|
* Initialize
|
|
*
|
|
* Checking for existance of the database file and creating it
|
|
*
|
|
* @return bool Is the database file exists?
|
|
*/
|
|
public static function initialize(): bool
|
|
{
|
|
if (file_exists(static::FILE)) {
|
|
// The database file exists
|
|
|
|
// Exit (success)
|
|
return true;
|
|
} else {
|
|
// The database file is not exists
|
|
|
|
// Creating the database file and exit (success/fail)
|
|
return touch(static::FILE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create
|
|
*
|
|
* Create records in the database file
|
|
*
|
|
* @param record $record The record
|
|
* @param array &$errors Buffer of errors
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function write(record $record, array &$errors = []): void
|
|
{
|
|
try {
|
|
// Opening the database file
|
|
$file = fopen(static::FILE, 'c');
|
|
|
|
if (flock($file, LOCK_EX)) {
|
|
// The file was locked
|
|
|
|
// Writing the serialized record to the database file
|
|
fwrite($file, $record->serialize());
|
|
|
|
// Applying changes
|
|
fflush($file);
|
|
|
|
// Unlocking the file
|
|
flock($file, LOCK_UN);
|
|
}
|
|
|
|
// Deinitializing unnecessary variables
|
|
unset($serialized, $record, $before);
|
|
|
|
// Closing the database file
|
|
fclose($file);
|
|
} catch (exception $e) {
|
|
// Write to the buffer of errors
|
|
$errors[] = [
|
|
'text' => $e->getMessage(),
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine(),
|
|
'stack' => $e->getTrace()
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read
|
|
*
|
|
* Read records in the database file
|
|
*
|
|
* @param int $amount Amount of records
|
|
* @param int $offset Offset of rows for start reading
|
|
* @param bool $backwards Read from end to beginning?
|
|
* @param callable|null $filter Filter for records function($record, $records): bool
|
|
* @param array &$errors Buffer of errors
|
|
*
|
|
* @return array|null Readed records
|
|
*/
|
|
public static function read(int $amount = 1, int $offset = 0, bool $backwards = false, ?callable $filter = null, array &$errors = []): ?array
|
|
{
|
|
try {
|
|
// Opening the database file
|
|
$file = fopen(static::FILE, 'r');
|
|
|
|
// Initializing the buffer of readed records
|
|
$records = [];
|
|
|
|
// Continuing reading
|
|
offset:
|
|
|
|
foreach (static::file(file: $file, offset: $offset, rows: $amount, position: 0, step: $backwards ? -1 : 1) as $row) {
|
|
// Iterating over rows
|
|
|
|
if ($row === null) {
|
|
// Reached the end or the beginning of the file
|
|
|
|
// Deinitializing unnecessary variables
|
|
unset($row, $record, $offset);
|
|
|
|
// Closing the database file
|
|
fclose($file);
|
|
|
|
// Exit (success)
|
|
return $records;
|
|
}
|
|
|
|
// Initializing record
|
|
$record = new record($row)->combine($this);
|
|
|
|
if ($record) {
|
|
// Initialized record
|
|
|
|
if ($filter === null || $filter($record, $records)) {
|
|
// Filter passed
|
|
|
|
// Writing to the buffer of readed records
|
|
$records[] = $record;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Deinitializing unnecessary variables
|
|
unset($row, $record);
|
|
|
|
if (count($records) < $amount) {
|
|
// Fewer rows were read than requested
|
|
|
|
// Writing offset for reading
|
|
$offset += $amount;
|
|
|
|
// Continuing reading (enter to the recursion)
|
|
goto offset;
|
|
}
|
|
|
|
// Deinitializing unnecessary variables
|
|
unset($offset);
|
|
|
|
|
|
// Closing the database file
|
|
fclose($file);
|
|
|
|
// Exit (success)
|
|
return $records;
|
|
} catch (exception $e) {
|
|
// Write to the buffer of errors
|
|
$errors[] = [
|
|
'text' => $e->getMessage(),
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine(),
|
|
'stack' => $e->getTrace()
|
|
];
|
|
}
|
|
|
|
// Exit (fail)
|
|
return null;
|
|
}
|
|
}
|