move from mirzaev/repression

This commit is contained in:
2024-11-23 09:50:05 +07:00
parent a24038adfb
commit fb5738c4b0
8 changed files with 386 additions and 0 deletions

View File

@@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
namespace mirzaev\csv\interfaces;
// Files of the project
use mirzaev\csv\traits\csv as csv_trait;
/**
* CSV
*
* Comma-Separated Values by RFC 4180
*
* @see https://tools.ietf.org/html/rfc4180
*
* @used-by csv_trait
* @package mirzaev\csv\interfaces
*
* @var string FILE Path to the database file
*
* @method void static write() Write to the database file
* @method array|null static read() Read from the database file
* @method string|false static serialize() Preparing data for writing to the database
* @method array|false static deserialize() Preparing data from the database to processing
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
interface csv
{
/**
* 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';
/**
* Write
*
* Write to the database file
*
* @return void
*/
public static function write(): void;
/**
* Read
*
* Read from the start of the database file
*
* @param int $rows Amount of rows for reading
*
* @return array|null Readed records
*/
public static function read(int $rows = 1): ?array;
/**
* Last
*
* Read from the end of the database file
*
* @param int $rows Amount of rows for reading
*
* @return array|null Readed records
*/
public static function last(int $rows = 1): ?array;
/**
* Serialize
*
* Preparing data for writing to the database
*
* @param array $parameters Values for serializing
*
* @return string|false Serialized data
*/
public static function serialize(array $parameters): string|false;
/**
* Deserialize
*
* Preparing data from the database to processing
*
* @param string $row Record for deserializing
*
* @return array|false Serialized data
*/
public static function deserialize(string $row): array|false;
}

134
mirzaev/csv/system/traits/csv.php Executable file
View File

@@ -0,0 +1,134 @@
<?php
declare(strict_types=1);
namespace mirzaev\csv\traits;
// Files of the project
use mirzaev\csv\interfaces\csv as csv_interface;
// Built-in libraries
use exception;
/**
* CSV
*
* Comma-Separated Values by RFC 4180
*
* @see https://tools.ietf.org/html/rfc4180
*
* @uses csv_interface
* @package mirzaev\csv\traits
*
* @method static array|null read(int $rows, array &$errors) Read from the start of the database file
* @method static string|false serialize(array $parameters, bool $created) Preparing data for writing to the database
* @method static array|false deserialize(string $row) Preparing data from the database to processing
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
trait csv
{
/**
* Read
*
* Read from the start of the database file
*
* @param int $rows Amount of rows for reading
* @param array &$errors Buffer of errors
*
* @return array|null Readed records
*/
public static function read(int $rows = 0, &$errors = []): ?array
{
try {
// Initializing the buffer of readed records
$records = [];
// Opening the file with views records
$file = fopen(static::FILE, 'c+');
while (--$rows >= 0 && ($row = fgets($file, 4096)) !== false) {
// Iterating over rows (records)
// Deserealizing record
$deserialized = static::deserialize($row);
if ($deserialized) {
// Deserialized record
// Writing to the buffer of readed records
$records[] = $deserialized;
}
}
// Closing file with views records
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;
}
/**
* Serialize
*
* Preparing data for writing to the database
*
* @param array $parameters Values for serializing
* @param bool $created Add date of creating at the end?
*
* @return string|false Serialized data
*/
public static function serialize(array $parameters, bool $created = false): string|false
{
// Declaring the buffer of serialized values
$serialized = '';
// Sanitizing values
foreach ($parameters as $value) $serialized .= ',' . preg_replace('/(?<=[^^])"(?=[^$])/', '""', preg_replace('/(?<=[^^]),(?=[^$])/', '\,', $value ?? ''));
// Writing date of creating to the buffer of serialized values
if ($created) $serialized .= ',' . time();
// Trimming excess first comma in the buffer of serialized values
$serialized = mb_substr($serialized, 1, mb_strlen($serialized));
// Exit (success/fail)
return empty($serialized) ? false : $serialized;
}
/**
* Deserialize
*
* Preparing data from the database to processing
*
* @param string $row Record for deserializing
*
* @return array|false Deserialized data
*/
public static function deserialize(string $row): array|false
{
// Separating row by commas
preg_match_all('/(?:^|,)(?=[^"]|(")?)"?((?(1)[^"]*|[^,"]*))"?(?=,|$)/', $row, $matches);
// Converting double quotes to single quotes
foreach ($matches[2] as &$match)
if (empty($match = preg_replace('/[\n\r]/', '', preg_replace('/""/', '"', preg_replace('/\\\,/', ',', trim((string) $match, '"'))))))
$match = null;
// Exit (success/fail)
return empty($matches[2]) ? false : $matches[2];
}
}

View File

@@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
namespace mirzaev\site\repression\models\traits;
// Built-in libraries
use Exception as exception,
Generator as generator;
/**
* File
*
* @package mirzaev\csv\traits
*
* @method static generator|null|false read($file, int $offset, int $rows, int $position, int $step, array &$errors) Read the file
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
trait file
{
/**
* Read
*
* Read the file
*
* @param resource $file Pointer to the file (fopen())
* @param int $offset Offset of rows for start reading
* @param int $rows Amount of rows for reading
* @param int $position Initial cursor position on a row
* @param int $step Reading step
* @param array &$errors Buffer of errors
*
* @return generator|null|false
*/
private static function read($file, int $offset = 0, int $rows = 10, int $position = 0, int $step = 1, array &$errors = []): generator|null|false
{
try {
while ($offset-- > 0) {
do {
// Iterate over symbols of the row
// The end (or the beginning) of the file reached (success)
if (feof($file)) break;
// Moving the cursor to next position on the row
fseek($file, $position += $step, SEEK_END);
// Reading a character of the row
$character = fgetc($file);
// Is the character a carriage return? (end or start of the row)
} while ($character !== PHP_EOL);
}
while ($rows-- > 0) {
// Reading rows
// Initializing of the buffer of row
$row = '';
// Initializing the character buffer to generate $row
$character = '';
do {
// Iterate over symbols of the row
// The end (or the beginning) of the file reached (success)
if (feof($file)) break;
// Building the row
$row = $step > 0 ? $row . $character : $character . $row;
// Moving the cursor to next position on the row
fseek($file, $position += $step, SEEK_END);
// Reading a character of the row
$character = fgetc($file);
// Is the character a carriage return? (end or start of the row)
} while ($character !== PHP_EOL);
// Exit (success)
yield empty($row) ? null : $row;
}
// Exit (success)
return null;
} 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 false;
}
}