Compare commits
	
		
			25 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 22fe7f894f | |||
| ec2721dc77 | |||
| 93078df174 | |||
| e263eb3f0b | |||
| 260be006ae | |||
| 6937c70c9c | |||
| d0a661016b | |||
| dd6fc42732 | |||
| 1cd4fe8a9a | |||
| d7e3a61e63 | |||
| 1ea9c1f41f | |||
| 273cc1ac2d | |||
| 6d332c7cf9 | |||
| 19978574a3 | |||
| c6fd8cf9f9 | |||
| 00e4b256f7 | |||
| 54b7ab07f1 | |||
| a9f90966e0 | |||
| c0ef644632 | |||
| 23e03fef14 | |||
| 65d729d6a9 | |||
| f56de9b053 | |||
| a69c2bbcdd | |||
| 66c39be90b | |||
| 24ef880439 | 
							
								
								
									
										6
									
								
								.editorconfig
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										6
									
								
								.editorconfig
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					root = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[README.md]
 | 
				
			||||||
 | 
					charset = utf-8
 | 
				
			||||||
 | 
					indent_style = tab
 | 
				
			||||||
 | 
					tab_width = 2
 | 
				
			||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										
										
										Normal file → Executable file
									
								
							@@ -1 +1,2 @@
 | 
				
			|||||||
vendor
 | 
					vendor
 | 
				
			||||||
 | 
					composer.lock
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										69
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								README.md
									
									
									
									
									
								
							@@ -1,2 +1,69 @@
 | 
				
			|||||||
# csv
 | 
					# Baza
 | 
				
			||||||
 | 
					Lightweight binary database by pure PHP<br>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Dependencies
 | 
				
			||||||
 | 
					1. [PHP 8.4](https://www.php.net/releases/8.4/en.php)
 | 
				
			||||||
 | 
					2. [Composer](https://getcomposer.org/) (php package manager)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Installation
 | 
				
			||||||
 | 
					`composer require mirzaev/baza`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Example
 | 
				
			||||||
 | 
					```php
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use mirzaev\baza\database,
 | 
				
			||||||
 | 
					    mirzaev\baza\column,
 | 
				
			||||||
 | 
					    mirzaev\baza\record,
 | 
				
			||||||
 | 
					    mirzaev\baza\enumerations\encoding,
 | 
				
			||||||
 | 
					    mirzaev\baza\enumerations\type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Initializing the database
 | 
				
			||||||
 | 
					$database = new database()
 | 
				
			||||||
 | 
					    ->encoding(encoding::utf8)
 | 
				
			||||||
 | 
					    ->columns(
 | 
				
			||||||
 | 
					        new column('name', type::string, ['length' => 32]),
 | 
				
			||||||
 | 
					        new column('second_name', type::string, ['length' => 64]),
 | 
				
			||||||
 | 
					        new column('age', type::integer),
 | 
				
			||||||
 | 
					        new column('height', type::float)
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    ->connect(__DIR__ . DIRECTORY_SEPARATOR . 'database.ba');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Initializing the record
 | 
				
			||||||
 | 
					$record = $database->record(
 | 
				
			||||||
 | 
					    'Arsen',
 | 
				
			||||||
 | 
					    'Mirzaev',
 | 
				
			||||||
 | 
					    23,
 | 
				
			||||||
 | 
					    (float) 165
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if ($database->write($record)) {
 | 
				
			||||||
 | 
					    // Writed the record into the database
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Updating the record in the database
 | 
				
			||||||
 | 
					    $updated = $database->read(
 | 
				
			||||||
 | 
					        filter: fn($record) => $record->name === 'Arsen', 
 | 
				
			||||||
 | 
					        update: fn(&$record) => $record->age = 24, 
 | 
				
			||||||
 | 
					        amount: 1
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Reading the record from the database
 | 
				
			||||||
 | 
					    $readed = $database->read(
 | 
				
			||||||
 | 
					        filter: fn($record) => $record->name === 'Arsen' && $record->age === 24,
 | 
				
			||||||
 | 
					        amount: 1
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Deleting the record from the database
 | 
				
			||||||
 | 
					    $deleted = $database->read(
 | 
				
			||||||
 | 
					        filter: fn($record) => $record->age < 25,
 | 
				
			||||||
 | 
					        delete: true,
 | 
				
			||||||
 | 
					        amount: 1000
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					?>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Used by
 | 
				
			||||||
 | 
					- My site-article about how i was kidnapped by PMC Wagner operatives [mirzaev/repression](https://git.svoboda.works/mirzaev/repression)
 | 
				
			||||||
 | 
					- My decentralized P2P blockchain chats project [mirzaev/notchat](https://git.svoboda.works/mirzaev/notchat)
 | 
				
			||||||
 | 
					- Svoboda Telegram chat-robot negotiator [svoboda/negotiator](https://git.svoboda.works/svoboda/negotiator)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,12 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"name": "mirzaev/csv",
 | 
						"name": "mirzaev/baza",
 | 
				
			||||||
	"description": "Lightweight library for creating CSV databases",
 | 
						"description": "Lightweight binary database by pure PHP",
 | 
				
			||||||
	"homepage": "https://git.mirzaev.sexy/mirzaev/csv",
 | 
						"homepage": "https://git.svoboda.works/mirzaev/baza",
 | 
				
			||||||
	"type": "library",
 | 
						"type": "database",
 | 
				
			||||||
	"keywords": [
 | 
						"keywords": [
 | 
				
			||||||
		"csv",
 | 
							"binary",
 | 
				
			||||||
		"database"
 | 
							"plain",
 | 
				
			||||||
 | 
							"lightweight"
 | 
				
			||||||
	],
 | 
						],
 | 
				
			||||||
	"readme": "README.md",
 | 
						"readme": "README.md",
 | 
				
			||||||
	"license": "WTFPL",
 | 
						"license": "WTFPL",
 | 
				
			||||||
@@ -19,21 +20,24 @@
 | 
				
			|||||||
	],
 | 
						],
 | 
				
			||||||
	"support": {
 | 
						"support": {
 | 
				
			||||||
		"email": "arsen@mirzaev.sexy",
 | 
							"email": "arsen@mirzaev.sexy",
 | 
				
			||||||
		"wiki": "https://git.mirzaev.sexy/mirzaev/csv/wiki",
 | 
							"wiki": "https://git.svoboda.works/mirzaev/baza/wiki",
 | 
				
			||||||
		"issues": "https://git.mirzaev.sexy/mirzaev/csv/issues"
 | 
							"issues": "https://git.svoboda.works/mirzaev/baza/issues"
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	"minimum-stability": "stable",
 | 
						"minimum-stability": "stable",
 | 
				
			||||||
	"require": {
 | 
						"require": {
 | 
				
			||||||
		"php": "^8.4"
 | 
							"php": "^8.4"
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
						"suggest": {
 | 
				
			||||||
 | 
							"mirzaev/record": "Active Record pattern"
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
	"autoload": {
 | 
						"autoload": {
 | 
				
			||||||
		"psr-4": {
 | 
							"psr-4": {
 | 
				
			||||||
			"mirzaev\\csv\\": "mirzaev/csv/system/"
 | 
								"mirzaev\\baza\\": "mirzaev/baza/system/"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
  "autoload-dev": {
 | 
					  "autoload-dev": {
 | 
				
			||||||
    "psr-4": {
 | 
					    "psr-4": {
 | 
				
			||||||
      "mirzaev\\csv\\tests\\": "mirzaev/csv/tests"
 | 
					      "mirzaev\\baza\\tests\\": "mirzaev/baza/tests"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										20
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							@@ -1,20 +0,0 @@
 | 
				
			|||||||
{
 | 
					 | 
				
			||||||
    "_readme": [
 | 
					 | 
				
			||||||
        "This file locks the dependencies of your project to a known state",
 | 
					 | 
				
			||||||
        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
 | 
					 | 
				
			||||||
        "This file is @generated automatically"
 | 
					 | 
				
			||||||
    ],
 | 
					 | 
				
			||||||
    "content-hash": "70ef8045ba581d96d3a68483b6031a33",
 | 
					 | 
				
			||||||
    "packages": [],
 | 
					 | 
				
			||||||
    "packages-dev": [],
 | 
					 | 
				
			||||||
    "aliases": [],
 | 
					 | 
				
			||||||
    "minimum-stability": "stable",
 | 
					 | 
				
			||||||
    "stability-flags": {},
 | 
					 | 
				
			||||||
    "prefer-stable": false,
 | 
					 | 
				
			||||||
    "prefer-lowest": false,
 | 
					 | 
				
			||||||
    "platform": {
 | 
					 | 
				
			||||||
        "php": "^8.4"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "platform-dev": {},
 | 
					 | 
				
			||||||
    "plugin-api-version": "2.6.0"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										152
									
								
								mirzaev/baza/system/column.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										152
									
								
								mirzaev/baza/system/column.php
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,152 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace mirzaev\baza;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Files of the project
 | 
				
			||||||
 | 
					use mirzaev\baza\database,
 | 
				
			||||||
 | 
						mirzaev\baza\enumerations\type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Built-in libraries
 | 
				
			||||||
 | 
					use DomainException as exception_domain,
 | 
				
			||||||
 | 
						LogicException as exception_logic,
 | 
				
			||||||
 | 
						InvalidArgumentException as exceptiin_invalid_argument;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Column
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @package mirzaev\baza
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @var string $name Name of the column
 | 
				
			||||||
 | 
					 * @var type $type Type of the column values
 | 
				
			||||||
 | 
					 * @var int $length Length of every binary value that will be written to the database file
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @method void __construct(string $name, type $type, array $parameters) Constructor
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @license http://www.wtfpl.net Do What The Fuck You Want To Public License
 | 
				
			||||||
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class column
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Name
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @var string $name Name of the column
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public readonly protected(set) string $name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Type
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @see https://www.php.net/manual/en/function.pack.php Pack (types are shown here)
 | 
				
			||||||
 | 
						 * @see https://www.php.net/manual/en/function.unpack.php Unpack
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @var type $type Type of the column values
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public readonly protected(set) type $type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Length
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Length of every binary value that will be written to the database file
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @throws exception_logic if the `length` property is already initialized
 | 
				
			||||||
 | 
						 * @throws exception_logic if the type is not initialized
 | 
				
			||||||
 | 
						 * @throws exception_domain if the type has fixed length
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @var int $length Binary length of the column
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public protected(set) int $length {
 | 
				
			||||||
 | 
							// Write
 | 
				
			||||||
 | 
							set(int $value) {
 | 
				
			||||||
 | 
								if (isset($this->length)) {
 | 
				
			||||||
 | 
									// Already been initialized
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Exit (fail)
 | 
				
			||||||
 | 
									throw new exception_logic('The `length` property is already initialized');
 | 
				
			||||||
 | 
								} else if (!isset($this->type)) {
 | 
				
			||||||
 | 
									// The type is not initialized
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Exit (fail)
 | 
				
			||||||
 | 
									throw new exception_logic('The type of the column values is not initialized');
 | 
				
			||||||
 | 
								} else if ($this->type === type::string) {
 | 
				
			||||||
 | 
									// String
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Writing into the property
 | 
				
			||||||
 | 
									$this->length = $value;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// Other type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Exit (fail)
 | 
				
			||||||
 | 
									throw new exception_domain('The "' . $this->type->name . '" type has fixed ' . $this->type->size() . ' bit length');
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Constructor
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param string $name Name of the column
 | 
				
			||||||
 | 
						 * @param type $type Type of the column values
 | 
				
			||||||
 | 
						 * @param array $parameters Parameters of the column (length, segments)
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return void
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function __construct(string $name, type $type, array $parameters = [])
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Writing into the property
 | 
				
			||||||
 | 
							$this->name = $name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Writing into the property
 | 
				
			||||||
 | 
							$this->type = $type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							foreach ($parameters as $name => $value) {
 | 
				
			||||||
 | 
								// Iterating over parameters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (property_exists($this, $name)) {
 | 
				
			||||||
 | 
									// Found the property
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Writing into the property
 | 
				
			||||||
 | 
									$this->{$name} = $value;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// Not found the property
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Exit (fail)
 | 
				
			||||||
 | 
									throw new exceptiin_invalid_argument("Not found the property: $name");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Pack
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param string|int|float $value Data to packing
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return string|false Packed binary value
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function pack(string|int|float $value): string|false
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Exit (success)
 | 
				
			||||||
 | 
							return match ($this->type) {
 | 
				
			||||||
 | 
								type::string => pack($this->type->value . $this->length, $value),
 | 
				
			||||||
 | 
								default =>	pack($this->type->value, $value)
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Unpack
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param string $binary Packed binary data
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return string|false Unpacked value
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function unpack(string $binary): string|int|float|false
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Exit (success)
 | 
				
			||||||
 | 
							return match ($this->type) {
 | 
				
			||||||
 | 
								type::string => unpack($this->type->value . $this->length, $binary ?? str_repeat("\0", $this->length))[1],
 | 
				
			||||||
 | 
								default => unpack($this->type->value, $binary ?? "\0")[1]
 | 
				
			||||||
 | 
							} ?? false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										684
									
								
								mirzaev/baza/system/database.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										684
									
								
								mirzaev/baza/system/database.php
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,684 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace mirzaev\baza;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Files of the project
 | 
				
			||||||
 | 
					use mirzaev\baza\enumerations\architecture,
 | 
				
			||||||
 | 
						mirzaev\baza\enumerations\encoding,
 | 
				
			||||||
 | 
						mirzaev\baza\enumerations\type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Built-in libraries
 | 
				
			||||||
 | 
					use LogicException as exception_logic,
 | 
				
			||||||
 | 
						InvalidArgumentException as exception_invalid_argument,
 | 
				
			||||||
 | 
						RuntimeException as exception_runtime,
 | 
				
			||||||
 | 
						DomainException as exception_domain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Database
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @package mirzaev\baza
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @var string $database Path to the database file
 | 
				
			||||||
 | 
					 * @var string $backups Path to the backups files directory
 | 
				
			||||||
 | 
					 * @var encoding $encoding Encoding of records in the database file
 | 
				
			||||||
 | 
					 * @var array $columns The database columns
 | 
				
			||||||
 | 
					 * @var int $length Binary size of every record in the database file
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @method self encoding(encoding $encoding) Write encoding into the database instance property (fluent interface)
 | 
				
			||||||
 | 
					 * @method self columns(column ...$columns) Write columns into the database instance property (fluent interface)
 | 
				
			||||||
 | 
					 * @method self connect(string $database) Initialize the database files (fluent interface)
 | 
				
			||||||
 | 
					 * @method record|null record(...$values) Initialize the record by the database columns
 | 
				
			||||||
 | 
					 * @method string pack(record $record) Pack the record values
 | 
				
			||||||
 | 
					 * @method record unpack(array $binaries) Unpack binary values and implement them as a `record` instance
 | 
				
			||||||
 | 
					 * @method bool write(record $record) Write the record into the database file
 | 
				
			||||||
 | 
					 * @method array read(?callable $filter, ?callable $update, bool $delete, int $amount, int $offset) Read records from the database file
 | 
				
			||||||
 | 
					 * @method bool backups() Initialize the backups files directory
 | 
				
			||||||
 | 
					 * @method int|false save() Create an unique backup file from the database file
 | 
				
			||||||
 | 
					 * @method bool load(int $identifier) Restore the database file from the backup file
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @license http://www.wtfpl.net Do What The Fuck You Want To Public License
 | 
				
			||||||
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class database
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Database
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Path to the database file
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @var string $database Path to the database file
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public protected(set) string $database = __DIR__ . DIRECTORY_SEPARATOR . 'database.baza';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Backups
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Path to the backups files directory
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @var string $backups Path to the backups files directory
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public protected(set) string $backups = __DIR__ . DIRECTORY_SEPARATOR . 'backups';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Encoding 
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @var encoding $encoding Encoding of records in the database file
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public protected(set) encoding $encoding;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Columns
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @var record[] $columns The database columns
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public protected(set) array $columns;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Length
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @var int $length Binary size of every record in the database file
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public protected(set) int $length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Architecture
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @var architecture $architecture Architecture of the CPU
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public architecture $architecture;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Constructor
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @throws exception_domain If failed to detemine the system architecture
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function __construct()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							$this->architecture = match (PHP_INT_SIZE) {
 | 
				
			||||||
 | 
								4 => architecture::x86,
 | 
				
			||||||
 | 
								8 => architecture::x86_64,
 | 
				
			||||||
 | 
								default => throw new exception_domain('Failed to determine the system architecture'),
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Encoding
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Write encoding into the database instance property
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @see https://en.wikipedia.org/wiki/Fluent_interface#PHP Fluent Interface
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param encoding $encoding The database file encoding
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return self The database instance (fluent interface)
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function encoding(encoding $encoding): self
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Writing into the database instance property
 | 
				
			||||||
 | 
							$this->encoding = $encoding;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Exit (success)
 | 
				
			||||||
 | 
							return $this;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Columns
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Write columns into the database instance property
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @see https://en.wikipedia.org/wiki/Fluent_interface#PHP Fluent Interface
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param column[] ...$columns The database columns
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return self The database instance (fluent interface)
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function columns(column ...$columns): self
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Writing into the database instance property
 | 
				
			||||||
 | 
							$this->columns = $columns;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Initializing the database instance property
 | 
				
			||||||
 | 
							$this->length ??= 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							foreach ($this->columns as $column) {
 | 
				
			||||||
 | 
								// Iterating over columns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if ($column->type === type::string) {
 | 
				
			||||||
 | 
									// String
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Adding the column binary length into the database instance property
 | 
				
			||||||
 | 
									$this->length += $column->length;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// Other types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Adding the column type size to the database instance property
 | 
				
			||||||
 | 
									$this->length += $column->type->size();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Exit (success)
 | 
				
			||||||
 | 
							return $this;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Connect 
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Initialize the database files
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @see https://en.wikipedia.org/wiki/Fluent_interface#PHP Fluent Interface
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param string $database Path to the database file
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return self The database instance (fluent interface)
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function connect(string $database): self
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Writing into the database instance property
 | 
				
			||||||
 | 
							$this->database = $database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Exit (success)
 | 
				
			||||||
 | 
							return $this;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Record
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Initialize the record by the database columns
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param mixed[] $values Values of the record
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @throws exceptiin_invalid_argument if the balue type not matches the column values types
 | 
				
			||||||
 | 
						 * @throws exception_logic if amount of columns not matches the amount of values
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return record|null The record instance
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function record(string|int|float ...$values): ?record
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (count($values) === count($this->columns)) {
 | 
				
			||||||
 | 
								// Amount of values matches amount of columns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Declaring the buffer of combined values
 | 
				
			||||||
 | 
								$combined = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								foreach ($this->columns as $index => $column) {
 | 
				
			||||||
 | 
									// Iterating over columns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (gettype($values[$index]) === $column->type->abstract()) {
 | 
				
			||||||
 | 
										// The value type matches the column values type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Writing named index value into the buffer of combined values
 | 
				
			||||||
 | 
										$combined[$column->name] = $values[$index];
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										// The value type not matches the column values type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Exit (fail)
 | 
				
			||||||
 | 
										throw new exception_invalid_argument('The value type not matches the column values type');
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Initializing the record by the buffer of combined values
 | 
				
			||||||
 | 
								$record = new record(...$combined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Exit (success)
 | 
				
			||||||
 | 
								return $record;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// Amount of values not matches amount of columns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Exit (fail)
 | 
				
			||||||
 | 
								throw new exception_logic('Amount of values not matches amount of columns');
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Exit (fail)
 | 
				
			||||||
 | 
							return null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Pack
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Pack the record values
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param record $record The record
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return string Packed values
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function pack(record $record): string
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Declaring buffer of packed values
 | 
				
			||||||
 | 
							$packed = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							foreach ($this->columns as $column) {
 | 
				
			||||||
 | 
								// Iterating over columns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if ($column instanceof column) {
 | 
				
			||||||
 | 
									// Initialized the column
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Initializing the record value
 | 
				
			||||||
 | 
									$value = $record->values()[$column->name];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (isset($value)) {
 | 
				
			||||||
 | 
										// Initialized the record value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if ($column->type === type::string) {
 | 
				
			||||||
 | 
											// String
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											// Converting to the database encoding
 | 
				
			||||||
 | 
											$value = mb_convert_encoding($value, $this->encoding->value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											// Packung the value and writing into the buffer of packed values
 | 
				
			||||||
 | 
											$packed .= $column->pack($value);
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											// Other types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											// Packung the value and writing into the buffer of packed values
 | 
				
			||||||
 | 
											$packed .= $column->pack($value);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Exit (success)
 | 
				
			||||||
 | 
							return $packed;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Unpack
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * Unpack binary values and implement them as a `record` instance
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param array $binaries Binary values in the same order as the columns
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return record The unpacked record from binary values
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function unpack(array $binaries): record
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Declaring the buffer of unpacked values
 | 
				
			||||||
 | 
							$unpacked = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							foreach ($this->columns as $index => $column) {
 | 
				
			||||||
 | 
								// Iterating over columns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if ($column instanceof column) {
 | 
				
			||||||
 | 
									// Initialized the column
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Initializing link to the binary value
 | 
				
			||||||
 | 
									$binary = $binaries[$index] ?? null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if ($column->type === type::string) {
 | 
				
			||||||
 | 
										// String
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Unpacking the value
 | 
				
			||||||
 | 
										$value = $column->unpack($binary);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Deleting NULL-characters
 | 
				
			||||||
 | 
										$unnulled = str_replace("\0", '', $value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Encoding the unpacked value
 | 
				
			||||||
 | 
										$encoded = mb_convert_encoding($unnulled, $this->encoding->value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Writing into the buffer of readed values
 | 
				
			||||||
 | 
										$unpacked[] = $encoded;
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										// Other types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Writing into the buffer of readed values
 | 
				
			||||||
 | 
										$unpacked[] = $column->unpack($binary);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Implementing the record
 | 
				
			||||||
 | 
							$record = $this->record(...$unpacked);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Exit (success)
 | 
				
			||||||
 | 
							return $record;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Write
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Write the record into the database file
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param record $record The record
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @throws exception_runtime If failed to lock the file
 | 
				
			||||||
 | 
						 * @throws exception_runtime If failed to unlock the file
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return bool Is the record was writed into the end of the database file
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function write(record $record): bool
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								// Opening the database file
 | 
				
			||||||
 | 
								$file = fopen($this->database, 'ab');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (flock($file, LOCK_EX)) {
 | 
				
			||||||
 | 
									// The file was locked
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Packing the record values
 | 
				
			||||||
 | 
									$packed = $this->pack($record);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Writing the packed values to the database file
 | 
				
			||||||
 | 
									fwrite($file, $packed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Applying changes
 | 
				
			||||||
 | 
									fflush($file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (flock($file, LOCK_UN)) {
 | 
				
			||||||
 | 
										// The file was unlocked
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Exit (success)
 | 
				
			||||||
 | 
										return true;
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										// Failed to unlock the file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Exit (fail)
 | 
				
			||||||
 | 
										throw new exception_runtime('Failed to unlock the file');
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// Failed to lock the file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Exit (fail)
 | 
				
			||||||
 | 
									throw new exception_runtime('Failed to lock the file');
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} finally {
 | 
				
			||||||
 | 
								// Closing the database file
 | 
				
			||||||
 | 
								fclose($file);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Exit (fail)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Read
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Read records from the database file
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Order: `$filter` -> `$offset` -> (`$delete` -> read deleted || `$update` -> read updated || read) -> `$amount`
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param callable|null $filter Filtering records `function($record, $records): bool`
 | 
				
			||||||
 | 
						 * @param callable|null $update Updating records `function(&$record): void`
 | 
				
			||||||
 | 
						 * @param callable|null $delete Deleting records
 | 
				
			||||||
 | 
						 * @param int $amount Amount iterator
 | 
				
			||||||
 | 
						 * @param int $offset Offset iterator
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return array|null Readed records
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function read(
 | 
				
			||||||
 | 
							?callable $filter = null,
 | 
				
			||||||
 | 
							?callable $update = null,
 | 
				
			||||||
 | 
							bool $delete = false,
 | 
				
			||||||
 | 
							int $amount = 1,
 | 
				
			||||||
 | 
							int $offset = 0
 | 
				
			||||||
 | 
						): ?array {
 | 
				
			||||||
 | 
							// Opening the database file
 | 
				
			||||||
 | 
							$file = fopen($this->database, 'c+b');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (flock($file, LOCK_EX)) {
 | 
				
			||||||
 | 
								// The file was locked
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Declaring the buffer of readed records
 | 
				
			||||||
 | 
								$records = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Declaring the buffer of failed to reading records
 | 
				
			||||||
 | 
								/* $failed = []; */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								while ($amount > 0) {
 | 
				
			||||||
 | 
									// Reading records
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Declaring the buffer of binary values
 | 
				
			||||||
 | 
									$binaries = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									foreach ($this->columns as $column) {
 | 
				
			||||||
 | 
										// Iterating over columns 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if ($column->type === type::string) {
 | 
				
			||||||
 | 
											// String
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											// Reading the binary value from the database
 | 
				
			||||||
 | 
											$binaries[] = fread($file, $column->length);
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											// Other types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											// Reading the binary value from the database
 | 
				
			||||||
 | 
											$binaries[] = fread($file, $column->type->size());
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Terminate loop when end of file is reached
 | 
				
			||||||
 | 
										/* if (feof($file)) break 2; */
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Terminate loop when end of file is reached
 | 
				
			||||||
 | 
									if (feof($file)) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									try {
 | 
				
			||||||
 | 
										// Unpacking the record
 | 
				
			||||||
 | 
										$record = $this->unpack($binaries);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if ((bool) array_filter($record->values())) {
 | 
				
			||||||
 | 
											// The record contains at least one non-empty value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											if (is_null($filter) || $filter($record, $records)) {
 | 
				
			||||||
 | 
												// Passed the filter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												if ($offset-- <= 0) {
 | 
				
			||||||
 | 
													// Offsetted
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													if ($delete) {
 | 
				
			||||||
 | 
														// Requested deleting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
														// Moving to the beginning of the row
 | 
				
			||||||
 | 
														fseek($file, -$this->length, SEEK_CUR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
														// Writing NUL-characters instead of the record to the database file
 | 
				
			||||||
 | 
														fwrite($file, str_repeat("\0", $this->length));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
														// Moving to the end of the row
 | 
				
			||||||
 | 
														fseek($file, $this->length, SEEK_CUR);
 | 
				
			||||||
 | 
													} else if ($update) {
 | 
				
			||||||
 | 
														// Requested updating
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
														// Updating the record
 | 
				
			||||||
 | 
														$update($record);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
														// Packing the updated record
 | 
				
			||||||
 | 
														$packed = $this->pack($record);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
														// Moving to the beginning of the row
 | 
				
			||||||
 | 
														fseek($file, -$this->length, SEEK_CUR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
														// Writing to the database file
 | 
				
			||||||
 | 
														fwrite($file, $packed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
														// Moving to the end of the row
 | 
				
			||||||
 | 
														fseek($file, $this->length, SEEK_CUR);
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													// Writing into the buffer of records
 | 
				
			||||||
 | 
													$records[] = $record;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													// Decreasing the amount iterator
 | 
				
			||||||
 | 
													--$amount;
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											// The record contains only empty values
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									} catch (exception_logic | exception_invalid_argument | exception_domain $exception) {
 | 
				
			||||||
 | 
										// Writing into the buffer of failed to reading records
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Exit (fail)
 | 
				
			||||||
 | 
										throw new exception_runtime('Failed to processing the record', previous: $exception);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Unlocking the file
 | 
				
			||||||
 | 
								flock($file, LOCK_UN);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Closing the database file
 | 
				
			||||||
 | 
								fclose($file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Exit (success)
 | 
				
			||||||
 | 
								return $records;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Exit (fail)
 | 
				
			||||||
 | 
							return null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Count
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @throws exception_runtime If the database is corrupted (counting result is float)
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return int Amount of records
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function count(): int
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Deleting the database file cache
 | 
				
			||||||
 | 
							clearstatcache(true, $this->database);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Counting
 | 
				
			||||||
 | 
							$amount = $this->length > 0 && file_exists($this->database) ? filesize($this->database) / $this->length : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Exit (success/fail)
 | 
				
			||||||
 | 
							return is_int($amount) ? $amount : throw new exception_runtime('The database is corrupted');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Backups
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Initialize the backups files directory
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @throws exception_runtime if failed to create the backups files directory
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return bool Is the backups files directory created?
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function backups(): bool
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (is_dir($this->backups) || is_writable($this->backups)) {
 | 
				
			||||||
 | 
								// The backups files directory exists
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Exit (success)
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// The backups files directory is not exists
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (mkdir(directory: $this->backups, permissions: 0775, recursive: true)) {
 | 
				
			||||||
 | 
									// The backups files directory created
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Exit (success)
 | 
				
			||||||
 | 
									return true;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// The backups files directory is still not exists
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Exit (fail)
 | 
				
			||||||
 | 
									throw new exception_runtime('Failed to create the backups files directory: "' . $this->backups . '"');
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Exit (fail)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Save
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Create an unique backup file from the database file
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @throws exception_runtime if failed to copying the database file to the backup file
 | 
				
			||||||
 | 
						 * @throws exception_runtime if failed to initialize the backups files directory
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return string|false Unique identifier of the created backup file
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function save(): string|false
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if ($this->backups()) {
 | 
				
			||||||
 | 
								// Initialized the backups files directory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Generation of unique identifier
 | 
				
			||||||
 | 
								generate:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Generating unique identifier for the backup file
 | 
				
			||||||
 | 
								$identifier = uniqid();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Initializing path to the backup file with generated identifier
 | 
				
			||||||
 | 
								$file = $this->backups . DIRECTORY_SEPARATOR . $identifier;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (file_exists($file)) {
 | 
				
			||||||
 | 
									// File with this identifier is already exists
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Repeating generation (entering into recursion)
 | 
				
			||||||
 | 
									goto generate;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// Generated unique identifier for the backup file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (copy($this->database, $file)) {
 | 
				
			||||||
 | 
										// Copied the database file to the backup file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Exit (success)
 | 
				
			||||||
 | 
										return $identifier;
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										// Not copied the database file to the backup file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Exit (fail)
 | 
				
			||||||
 | 
										throw new exception_runtime('Failed to copying the database file to the backup file');
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// Not initialized the backups files directory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Exit (fail)
 | 
				
			||||||
 | 
								throw new exception_runtime('Failed to initialize the backups files directory');
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Exit (fail)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Load
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Restore the database file from the backup file
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @throws exception_runtime if not found the backup file
 | 
				
			||||||
 | 
						 * @throws exception_runtime if failed to initialize the backups files directory
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return int|false Unique identifier of the created backup file
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function load(int $identifier): bool
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if ($this->backups()) {
 | 
				
			||||||
 | 
								// Initialized the backups files directory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Initializing path to the backup file
 | 
				
			||||||
 | 
								$file = $this->backups . DIRECTORY_SEPARATOR . $identifier;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (file_exists($file)) {
 | 
				
			||||||
 | 
									// Initialized the backup file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (rename($file, $this->database)) {
 | 
				
			||||||
 | 
										// Loaded the database file from the backup file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Exit (success)
 | 
				
			||||||
 | 
										return true;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// Not initialized the backup file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Exit (fail)
 | 
				
			||||||
 | 
									throw new exception_runtime('Not found the backup file');
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// Not initialized the backups files directory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Exit (fail)
 | 
				
			||||||
 | 
								throw new exception_runtime('Failed to initialize the backups files directory');
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Exit (fail)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										97
									
								
								mirzaev/baza/system/enumerations/architecture.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								mirzaev/baza/system/enumerations/architecture.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace mirzaev\baza\enumerations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Architecture
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @see http://php.net/pack#109328 `pack()` works only with 32 bit commands
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @package mirzaev\baza\enumerations
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
 | 
				
			||||||
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					enum architecture: int
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						case x86 = 32;
 | 
				
			||||||
 | 
						case x86_64 = 64;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Packable
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return bool Is this architecture are packable without segmentating?
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function packable(): bool
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Exit (success)
 | 
				
			||||||
 | 
							return $this->value <= architecture::segment();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Segments
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Calculate the amount of `architecture::segment()` bit segments
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return int Amount of segments
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function segments(): int
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Calculating amount of segments
 | 
				
			||||||
 | 
							$amount = $this->value / architecture::segment();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Exit (success)
 | 
				
			||||||
 | 
							return $amount;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Segment
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @see http://php.net/pack#109328 `pack()` works only with 32 bit commands
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return int Length of a segment in bits
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static function segment(): int
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Exit (success)
 | 
				
			||||||
 | 
							return architecture::x86->value;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Segmentate
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Splits the value into `architecture::segment()` bit segments
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param int $value The value for segmentating
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return array Segments [int, int, int]
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @deprecated Just use `long long` type!
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function segmentate(int $value): array
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Calculating expected amount of segments 
 | 
				
			||||||
 | 
							$amount = $this->segments();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Initializing length of a segment
 | 
				
			||||||
 | 
							$segment = architecture::segment();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Declaring the buffer of generated segments
 | 
				
			||||||
 | 
							$segments = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for ($i = 0; $i < $amount; ++$i) {
 | 
				
			||||||
 | 
								// Iterating over generating segments
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Calculating offset from the right
 | 
				
			||||||
 | 
								$right = $amount - $i - 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Generating segments
 | 
				
			||||||
 | 
								$segments[] = ($value & (0xffffffff << ($segment * $right))) >> ($segment * $right);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Exit (success)
 | 
				
			||||||
 | 
							return $segments;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										76
									
								
								mirzaev/baza/system/enumerations/encoding.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										76
									
								
								mirzaev/baza/system/enumerations/encoding.php
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace mirzaev\baza\enumerations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Build-in libraries
 | 
				
			||||||
 | 
					use UnexpectedValueException as exception_unexpected_value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Encoding
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The selected encoding significantly affects the file size and the speed of working with it
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ASCII English
 | 
				
			||||||
 | 
					 * CP1250 Central Europe
 | 
				
			||||||
 | 
					 * CP1251 Cyrillic
 | 
				
			||||||
 | 
					 * CP1252 Western Europe
 | 
				
			||||||
 | 
					 * CP1253 Greek
 | 
				
			||||||
 | 
					 * CP1254 Turkish
 | 
				
			||||||
 | 
					 * CP1255 Hebrew
 | 
				
			||||||
 | 
					 * CP1256 Arabic
 | 
				
			||||||
 | 
					 * CP1257 Baltic Rim
 | 
				
			||||||
 | 
					 * CP1258 Vietnam
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * UTF-8 Length 1-4 bytes, better for Europe, backwards compatible with ASCII (minimum size)
 | 
				
			||||||
 | 
					 * UTF-16 Length 2-4 bytes, better for Asia (medium size with medium performance)
 | 
				
			||||||
 | 
					 * UTF-32 Fixed length of 4 bytes (maximum performance)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * In the database, the length for all encodings is fixed at the maximum value
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @see https://www.php.net/manual/ru/mbstring.supported-encodings.php Encodings in PHP
 | 
				
			||||||
 | 
					 * @see https://www.ascii-code.com/ About ASCII encodings
 | 
				
			||||||
 | 
					 * @see https://home.unicode.org/technical-quick-start-guide/ About unicode
 | 
				
			||||||
 | 
					 * @see https://www.unicode.org/main.html Abount unicode encodings
 | 
				
			||||||
 | 
					 * @see https://www.unicode.org/faq/utf_bom.html About UTF
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @package mirzaev\baza\enumerations
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
 | 
				
			||||||
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					enum encoding: string
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						case ascii = 'ASCII';
 | 
				
			||||||
 | 
						case cp1251 = 'CP1251'; // Windows 1251
 | 
				
			||||||
 | 
						case cp1252 = 'CP1252'; // Windows-1252
 | 
				
			||||||
 | 
						case cp1253 = 'CP1253'; // Windows-1253
 | 
				
			||||||
 | 
						case cp1254 = 'CP1254'; // Windows-1254
 | 
				
			||||||
 | 
						case cp1255 = 'CP1255'; // Windows-1255
 | 
				
			||||||
 | 
						case cp1256 = 'CP1256'; // Windows-1256
 | 
				
			||||||
 | 
						case cp1257 = 'CP1257'; // Windows-1257
 | 
				
			||||||
 | 
						case cp1258 = 'CP1258'; // Windows-1258
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case utf8 = 'UTF-8';
 | 
				
			||||||
 | 
						case utf16 = 'UTF-16';
 | 
				
			||||||
 | 
						case utf32 = 'UTF-32';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Length
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return int Number of bits for a symbol
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function maximum(): int
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Exit (success)
 | 
				
			||||||
 | 
							return match ($this) {
 | 
				
			||||||
 | 
								encoding::ascii => 7,
 | 
				
			||||||
 | 
								encoding::cp1251, encoding::cp1252, encoding::cp1253, encoding::cp1254, encoding::cp1255, encoding::cp1256, encoding::cp1257, encoding::cp1258 => 8,
 | 
				
			||||||
 | 
								encoding::utf8 => 8,
 | 
				
			||||||
 | 
								encoding::utf16 => 16,
 | 
				
			||||||
 | 
								encoding::utf32 => 32,
 | 
				
			||||||
 | 
								default => throw new exception_unexpected_value('Not found the encoding')
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										66
									
								
								mirzaev/baza/system/enumerations/type.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										66
									
								
								mirzaev/baza/system/enumerations/type.php
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace mirzaev\baza\enumerations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Build-in libraries
 | 
				
			||||||
 | 
					use UnexpectedValueException as exception_unexpected_value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Type
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @see https://www.php.net/pack Types
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @package mirzaev\baza\enumerations
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
 | 
				
			||||||
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					enum type: string
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						case string = 'a';
 | 
				
			||||||
 | 
						case char = 'c';
 | 
				
			||||||
 | 
						case char_unsigned = 'C';
 | 
				
			||||||
 | 
						case short = 's';
 | 
				
			||||||
 | 
						case short_unsigned = 'S';
 | 
				
			||||||
 | 
						case integer = 'i';
 | 
				
			||||||
 | 
						case integer_unsigned = 'I';
 | 
				
			||||||
 | 
						case long = 'l';
 | 
				
			||||||
 | 
						case long_unsigned = 'L';
 | 
				
			||||||
 | 
						case long_long = 'q';
 | 
				
			||||||
 | 
						case long_long_unsigned = 'Q';
 | 
				
			||||||
 | 
						case float = 'f';
 | 
				
			||||||
 | 
						case double = 'd';
 | 
				
			||||||
 | 
						case null = 'x';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Type
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @see https://www.php.net/manual/en/function.gettype.php (here is why "double" instead of "float" and "NULL" instead of "null")
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return string Type
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function abstract(): string
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Exit (success)
 | 
				
			||||||
 | 
							return match ($this) {
 | 
				
			||||||
 | 
								type::string, type::short => 'string',
 | 
				
			||||||
 | 
								type::char, type::char_unsigned, type::short_unsigned, type::integer, type::integer_unsigned, type::long, type::long_unsigned, type::long_long, type::long_long_unsigned => 'integer',
 | 
				
			||||||
 | 
								type::float, type::double => 'double',
 | 
				
			||||||
 | 
								type::null => 'NULL',
 | 
				
			||||||
 | 
								default => throw new exception_unexpected_value('Not found the type')
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Size
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return int Size in bytes
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function size(): int
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Exit (success)
 | 
				
			||||||
 | 
							return strlen(pack($this->value, 0));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										132
									
								
								mirzaev/baza/system/record.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										132
									
								
								mirzaev/baza/system/record.php
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,132 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace mirzaev\baza;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Built-in libraries
 | 
				
			||||||
 | 
					use DomainException as exception_domain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Record
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @package mirzaev\baza
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @var array $values The record values
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @method void __construct(string|int|float $values) Constructor
 | 
				
			||||||
 | 
					 * @method array values() Read all values of the record
 | 
				
			||||||
 | 
					 * @method void __set(string $name, mixed $value) Write the record value
 | 
				
			||||||
 | 
					 * @method mized __get(string $name) Read the record value
 | 
				
			||||||
 | 
					 * @method void  __unset(string $name) Delete the record value
 | 
				
			||||||
 | 
					 * @method bool __isset(string $name) Check for initializing of the record value
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @license http://www.wtfpl.net Do What The Fuck You Want To Public License
 | 
				
			||||||
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class record
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Values
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @var array $values The record values
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						protected array $values = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Constructor
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param string[]|int[]|float[] $values Values of the record
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return void
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function __construct(string|int|float ...$values)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Initializing values
 | 
				
			||||||
 | 
							if (!empty($values)) $this->values = $values;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Values
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Read all values of the record 
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return array All values of the record
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function values(): array
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return $this->values ?? [];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Write
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Write the value
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param string $name Name of the parameter
 | 
				
			||||||
 | 
						 * @param mixed $value Content of the parameter
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @throws exception_domain if not found the parameter
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return void
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function __set(string $name, mixed $value = null): void
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (isset($this->values[$name])) {
 | 
				
			||||||
 | 
								// Initialized the parameter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Writing the value and exit
 | 
				
			||||||
 | 
								$this->values[$name] = $value;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// Not initialized the parameter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Exit (fail)
 | 
				
			||||||
 | 
								throw new exception_domain("Not found the parameter: $name"); 
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Read
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * Read the value
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param string $name Name of the value
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return mixed Content of the value
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function __get(string $name): mixed
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Reading the value and exit (success)
 | 
				
			||||||
 | 
							return $this->values[$name] ?? null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Delete
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Delete the value
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param string $name Name of the value
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return void
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function __unset(string $name): void
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Deleting the value
 | 
				
			||||||
 | 
							unset($this->values[$name]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Check for initializing
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Check for initializing the value
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param string $name Name of the value
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return bool Is the value initialized?
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function __isset(string $name): bool
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Checking for initializing the value and exit (success)
 | 
				
			||||||
 | 
							return isset($this->values[$name]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1
									
								
								mirzaev/baza/tests/.gitignore
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										1
									
								
								mirzaev/baza/tests/.gitignore
									
									
									
									
										vendored
									
									
										Executable file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					temporary/*
 | 
				
			||||||
							
								
								
									
										428
									
								
								mirzaev/baza/tests/record.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										428
									
								
								mirzaev/baza/tests/record.php
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,428 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use mirzaev\baza\database,
 | 
				
			||||||
 | 
						mirzaev\baza\record,
 | 
				
			||||||
 | 
						mirzaev\baza\column,
 | 
				
			||||||
 | 
						mirzaev\baza\enumerations\encoding,
 | 
				
			||||||
 | 
						mirzaev\baza\enumerations\architecture,
 | 
				
			||||||
 | 
						mirzaev\baza\enumerations\type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Initializing path to the composer loader file (main project)
 | 
				
			||||||
 | 
					$autoload =
 | 
				
			||||||
 | 
						__DIR__ . DIRECTORY_SEPARATOR .
 | 
				
			||||||
 | 
						'..' . DIRECTORY_SEPARATOR .
 | 
				
			||||||
 | 
						'..' . DIRECTORY_SEPARATOR .
 | 
				
			||||||
 | 
						'..' . DIRECTORY_SEPARATOR .
 | 
				
			||||||
 | 
						'vendor' . DIRECTORY_SEPARATOR .
 | 
				
			||||||
 | 
						'autoload.php';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reinitializing path to the composer loaded file (depencendy project)
 | 
				
			||||||
 | 
					if (!file_exists($autoload))
 | 
				
			||||||
 | 
						$autoload =
 | 
				
			||||||
 | 
							__DIR__ . DIRECTORY_SEPARATOR .
 | 
				
			||||||
 | 
							'..' . DIRECTORY_SEPARATOR .
 | 
				
			||||||
 | 
							'..' . DIRECTORY_SEPARATOR .
 | 
				
			||||||
 | 
							'..' . DIRECTORY_SEPARATOR .
 | 
				
			||||||
 | 
							'..' . DIRECTORY_SEPARATOR .
 | 
				
			||||||
 | 
							'..' . DIRECTORY_SEPARATOR .
 | 
				
			||||||
 | 
							'autoload.php';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Importing files of thr project and dependencies
 | 
				
			||||||
 | 
					require($autoload);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Initializing path to the database file
 | 
				
			||||||
 | 
					$file = __DIR__ . DIRECTORY_SEPARATOR . 'temporary' . DIRECTORY_SEPARATOR . 'database.baza';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo "Started testing\n\n\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Initializing the counter of actions
 | 
				
			||||||
 | 
					$action = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if (!file_exists($file) || unlink($file)) {
 | 
				
			||||||
 | 
						// Deleted deprecated database
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						echo '[' . ++$action . '] ' . "Deleted deprecated database\n";
 | 
				
			||||||
 | 
					} else {
 | 
				
			||||||
 | 
						// Not deleted deprecated database
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						echo '[' . ++$action . '][FAIL] ' . "Failed to delete deprecated database\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						die;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Initializing the test database
 | 
				
			||||||
 | 
					$database = new database();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if ($database->architecture === architecture::x86_64) {
 | 
				
			||||||
 | 
						$database
 | 
				
			||||||
 | 
							->encoding(encoding::utf8)
 | 
				
			||||||
 | 
							->columns(
 | 
				
			||||||
 | 
								new column('name', type::string, ['length' => 32]),
 | 
				
			||||||
 | 
								new column('second_name', type::string, ['length' => 64]),
 | 
				
			||||||
 | 
								new column('age', type::integer),
 | 
				
			||||||
 | 
								new column('height', type::float),
 | 
				
			||||||
 | 
								// 64-bit values test
 | 
				
			||||||
 | 
								new column('neuron_count', type::integer_unsigned),
 | 
				
			||||||
 | 
								new column('motivation', type::double),
 | 
				
			||||||
 | 
								new column('reputation', type::integer),
 | 
				
			||||||
 | 
								new column('active', type::char)
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							->connect($file);
 | 
				
			||||||
 | 
					} else {
 | 
				
			||||||
 | 
						$database
 | 
				
			||||||
 | 
							->encoding(encoding::utf8)
 | 
				
			||||||
 | 
							->columns(
 | 
				
			||||||
 | 
								new column('name', type::string, ['length' => 32]),
 | 
				
			||||||
 | 
								new column('second_name', type::string, ['length' => 64]),
 | 
				
			||||||
 | 
								new column('age', type::integer),
 | 
				
			||||||
 | 
								new column('height', type::float),
 | 
				
			||||||
 | 
								new column('active', type::char)
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							->connect($file);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Initialized the database\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Initializing the record
 | 
				
			||||||
 | 
					if ($database->architecture === architecture::x86_64) {
 | 
				
			||||||
 | 
						$record = $database->record(
 | 
				
			||||||
 | 
							'Arsen',
 | 
				
			||||||
 | 
							'Mirzaev',
 | 
				
			||||||
 | 
							24,
 | 
				
			||||||
 | 
							165.5,
 | 
				
			||||||
 | 
							9100,
 | 
				
			||||||
 | 
							1.7976931348623E+238,
 | 
				
			||||||
 | 
							7355608,
 | 
				
			||||||
 | 
							1
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					} else {
 | 
				
			||||||
 | 
						$record = $database->record(
 | 
				
			||||||
 | 
							'Arsen',
 | 
				
			||||||
 | 
							'Mirzaev',
 | 
				
			||||||
 | 
							24,
 | 
				
			||||||
 | 
							165.5,
 | 
				
			||||||
 | 
							1
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Initialized the record\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Initializing the counter of tests
 | 
				
			||||||
 | 
					$test = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . '][' . ++$test . '][' . ($record->name === 'Arsen' ? 'SUCCESS' : 'FAIL') . "][\"name\"] Expected: \"Arsen\" (string). Actual: \"$record->name\" (" . gettype($record->name) . ")\n";
 | 
				
			||||||
 | 
					echo '[' . $action . '][' . ++$test . '][' . ($record->second_name === 'Mirzaev' ? 'SUCCESS' : 'FAIL') . "][\"second_name\"] Expected: \"Mirzaev\" (string). Actual: \"$record->second_name\" (" . gettype($record->second_name) . ")\n";
 | 
				
			||||||
 | 
					echo '[' . $action . '][' . ++$test . '][' . ($record->age === 24 ? 'SUCCESS' : 'FAIL') . "][\"age\"] Expected: \"24\" (integer). Actual: \"$record->age\" (" . gettype($record->age) . ")\n";
 | 
				
			||||||
 | 
					echo '[' . $action . '][' . ++$test . '][' . ($record->height === 165.5 ? 'SUCCESS' : 'FAIL') . "][\"height\"] Expected: \"165.5\" (double). Actual: \"$record->height\" (" . gettype($record->height) . ")\n";
 | 
				
			||||||
 | 
					if ($database->architecture === architecture::x86_64) {
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($record->neuron_count === 9100 ? 'SUCCESS' : 'FAIL') . "][\"neuron_count\"] Expected: \"9100\" (integer). Actual: \"$record->neuron_count\" (" . gettype($record->neuron_count) . ")\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($record->motivation === 1.7976931348623E+238 ? 'SUCCESS' : 'FAIL') . "][\"motivation\"] Expected: \"1.7976931348623E+238\" (double). Actual: \"$record->motivation\" (" . gettype($record->motivation) . ")\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($record->reputation === 7355608 ? 'SUCCESS' : 'FAIL') . "][\"reputation\"] Expected: \"7355608\" (integer). Actual: \"$record->reputation\" (" . gettype($record->reputation) . ")\n";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					echo '[' . $action . '][' . ++$test . '][' . ($record->active === 1 ? 'SUCCESS' : 'FAIL') . "][\"active\"] Expected: \"1\" (integer). Actual: \"$record->active\" (" . gettype($record->active) . ")\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . $action . "] The record parameters checks have been completed\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reinitializing the counter of tests
 | 
				
			||||||
 | 
					$test = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Writing the record into the database
 | 
				
			||||||
 | 
					$database->write($record);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Wrote the record into the database\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Initializing the second record
 | 
				
			||||||
 | 
					if ($database->architecture === architecture::x86_64) {
 | 
				
			||||||
 | 
						$record_ivan = $database->record(
 | 
				
			||||||
 | 
							'Ivan',
 | 
				
			||||||
 | 
							'Ivanov',
 | 
				
			||||||
 | 
							24,
 | 
				
			||||||
 | 
							(float) 210,
 | 
				
			||||||
 | 
							PHP_INT_MAX,
 | 
				
			||||||
 | 
							PHP_FLOAT_MIN,
 | 
				
			||||||
 | 
							PHP_INT_MIN,
 | 
				
			||||||
 | 
							0
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					} else {
 | 
				
			||||||
 | 
						$record_ivan = $database->record(
 | 
				
			||||||
 | 
							'Ivan',
 | 
				
			||||||
 | 
							'Ivanov',
 | 
				
			||||||
 | 
							24,
 | 
				
			||||||
 | 
							(float) 210,
 | 
				
			||||||
 | 
							0
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Initialized the record\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Writing the second record into the databasse
 | 
				
			||||||
 | 
					$database->write($record_ivan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Wrote the record into the database\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Initializing the second record
 | 
				
			||||||
 | 
					if ($database->architecture === architecture::x86_64) {
 | 
				
			||||||
 | 
						$record_margarita = $database->record(
 | 
				
			||||||
 | 
							'Margarita',
 | 
				
			||||||
 | 
							'Esenina',
 | 
				
			||||||
 | 
							19,
 | 
				
			||||||
 | 
							(float) 165,
 | 
				
			||||||
 | 
							89000000000,
 | 
				
			||||||
 | 
							(float) 163,
 | 
				
			||||||
 | 
							PHP_INT_MAX,
 | 
				
			||||||
 | 
							1
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					} else {
 | 
				
			||||||
 | 
						$record_margarita = $database->record(
 | 
				
			||||||
 | 
							'Margarita',
 | 
				
			||||||
 | 
							'Esenina',
 | 
				
			||||||
 | 
							19,
 | 
				
			||||||
 | 
							(float) 165,
 | 
				
			||||||
 | 
							1
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Initialized the record\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Writing the second record into the databasse
 | 
				
			||||||
 | 
					$database->write($record_margarita);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Wrote the record into the database\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reading all records from the database
 | 
				
			||||||
 | 
					$records_read_all = $database->read(amount: 99999);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Read all records from the database\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
						echo '[' . ++$action . '][' . ++$test . '][' . (gettype($records_read_all) === 'array' ? 'SUCCESS' : 'FAIL') . '][type of returned value] Expected: "array". Actual: "' . gettype($records_read_all) . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (count($records_read_all) === 3 ? 'SUCCESS' : 'FAIL') . '][amount of read records] Expected: 3 records. Actual: ' . count($records_read_all) . " records\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (gettype($records_read_all[0]) === 'object' ? 'SUCCESS' : 'FAIL') . '][type of read values] Expected: "object". Actual: "' .  gettype($records_read_all[0]) . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($records_read_all[0] instanceof record ? 'SUCCESS' : 'FAIL') . '][class of read object values] Expected: "' . record::class . '". Actual: "' .  $records_read_all[0]::class . "\"\n";
 | 
				
			||||||
 | 
						if ($database->architecture === architecture::x86_64) {
 | 
				
			||||||
 | 
							echo '[' . $action . '][' . ++$test . '][' . ($records_read_all[0]->neuron_count === 9100 ? 'SUCCESS' : 'FAIL') . ']["neuron_count"] Expected: "9100" (integer). Actual: "' . $records_read_all[0]->neuron_count . '" (' . gettype($records_read_all[0]->neuron_count) . ")\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						echo '[' . $action . "] The read all records checks have been completed\n";
 | 
				
			||||||
 | 
					} catch (exception $e) {
 | 
				
			||||||
 | 
						echo '[' . $action . "][WARNING] The read all records checks have been completed with errors\n";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reinitializing the counter of tests
 | 
				
			||||||
 | 
					$test = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reading the first record from the database
 | 
				
			||||||
 | 
					$record_read_first = $database->read(amount: 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Read the first record from the database\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
						echo '[' . ++$action . '][' . ++$test . '][' . (gettype($record_read_first) === 'array' ? 'SUCCESS' : 'FAIL') . '][type of returned value] Expected: "array". Actual: "' . gettype($record_read_first) . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (count($record_read_first) === 1 ? 'SUCCESS' : 'FAIL') . '][amount of read records] Expected: 1 records. Actual: ' . count($record_read_first) . " records\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (gettype($record_read_first[0]) === 'object' ? 'SUCCESS' : 'FAIL') . '][type of read values] Expected: "object". Actual: "' .  gettype($record_read_first[0]) . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($record_read_first[0] instanceof record ? 'SUCCESS' : 'FAIL') . '][class of read object values] Expected: "' . record::class . '". Actual: "' .  $record_read_first[0]::class . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($record_read_first[0]->second_name === 'Mirzaev' ? 'SUCCESS' : 'FAIL') . ']["second_name"] Expected: "Mirzaev" (string). Actual: "' . $record_read_first[0]->second_name . '" (' . gettype($record_read_first[0]->second_name) . ")\n";
 | 
				
			||||||
 | 
						if ($database->architecture === architecture::x86_64) {
 | 
				
			||||||
 | 
							echo '[' . $action . '][' . ++$test . '][' . ($record_read_first[0]->neuron_count === 9100 ? 'SUCCESS' : 'FAIL') . ']["neuron_count"] Expected: "9100" (integer). Actual: "' . $record_read_first[0]->neuron_count . '" (' . gettype($record_read_first[0]->neuron_count) . ")\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						echo '[' . $action . "] The read first record checks have been completed\n";
 | 
				
			||||||
 | 
					} catch (exception $e) {
 | 
				
			||||||
 | 
						echo '[' . $action . "][WARNING] The read first record checks have been completed with errors\n";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reinitializing the counter of tests
 | 
				
			||||||
 | 
					$test = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reading the second record from the database
 | 
				
			||||||
 | 
					$record_read_second = $database->read(amount: 1, offset: 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Read the second record from the database\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
						echo '[' . ++$action . '][' . ++$test . '][' . (gettype($record_read_second) === 'array' ? 'SUCCESS' : 'FAIL') . '][type of returned value] Expected: "array". Actual: "' . gettype($record_read_second) . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (count($record_read_second) === 1 ? 'SUCCESS' : 'FAIL') . '][amount of read records] Expected: 1 records. Actual: ' . count($record_read_second) . " records\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (gettype($record_read_second[0]) === 'object' ? 'SUCCESS' : 'FAIL') . '][type of read values] Expected: "object". Actual: "' .  gettype($record_read_second[0]) . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($record_read_second[0] instanceof record ? 'SUCCESS' : 'FAIL') . '][class of read object values] Expected: "' . record::class . '". Actual: "' .  $record_read_second[0]::class . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($record_read_second[0]->second_name === 'Ivanov' ? 'SUCCESS' : 'FAIL') . ']["second_name"] Expected: "Ivanov" (string). Actual: "' . $record_read_second[0]->second_name . '" (' . gettype($record_read_second[0]->second_name) . ")\n";
 | 
				
			||||||
 | 
						if ($database->architecture === architecture::x86_64) {
 | 
				
			||||||
 | 
							/**
 | 
				
			||||||
 | 
							 * Due to IEEE 754 double precision format double equality is problematic
 | 
				
			||||||
 | 
							 *
 | 
				
			||||||
 | 
							 * @see https://www.php.net/manual/en/language.types.float.php#113703
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							echo '[' . $action . '][' . ++$test . '][' . (round($record_read_second[0]->motivation, 2) === round(2.2250738585072E-308, 2) ? 'SUCCESS' : 'FAIL') . ']["motivation"] Expected: "2.2250738585072E-308" (double). Actual: "' . $record_read_second[0]->motivation . '" (' . gettype($record_read_second[0]->motivation) . ")\n";
 | 
				
			||||||
 | 
							echo '[' . $action . '][' . ++$test . '][' . ($record_read_second[0]->reputation === (int) 0 ? 'SUCCESS' : 'FAIL') . ']["reputation"] Expected: "0" (integer) (overflow). Actual: "' . $record_read_second[0]->reputation . '" (' . gettype($record_read_second[0]->reputation) . ")\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						echo '[' . $action . "] The read second record checks have been completed\n";
 | 
				
			||||||
 | 
					} catch (exception $e) {
 | 
				
			||||||
 | 
						echo '[' . $action . "][WARNING] The read second record checks have been completed with errors\n";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reinitializing the counter of tests
 | 
				
			||||||
 | 
					$test = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reading the record from the database by filter
 | 
				
			||||||
 | 
					$record_read_filter = $database->read(filter: fn($record) => $record?->second_name === 'Ivanov', amount: 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Read the record from the database by filter\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
						echo '[' . ++$action . '][' . ++$test . '][' . (gettype($record_read_filter) === 'array' ? 'SUCCESS' : 'FAIL') . '][type of returned value] Expected: "array". Actual: "' . gettype($record_read_filter) . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (count($record_read_filter) === 1 ? 'SUCCESS' : 'FAIL') . '][amount of read records] Expected: 1 records. Actual: ' . count($record_read_filter) . " records\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (gettype($record_read_filter[0]) === 'object' ? 'SUCCESS' : 'FAIL') . '][type of read values] Expected: "object". Actual: "' .  gettype($record_read_filter[0]) . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($record_read_filter[0] instanceof record ? 'SUCCESS' : 'FAIL') . '][class of read object values] Expected: "' . record::class . '". Actual: "' .  $record_read_filter[0]::class . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($record_read_filter[0]->second_name === 'Ivanov' ? 'SUCCESS' : 'FAIL') . ']["second_name"] Expected: "Ivanov" (string). Actual: "' . $record_read_filter[0]->second_name . '" (' . gettype($record_read_filter[0]->second_name) . ")\n";
 | 
				
			||||||
 | 
						if ($database->architecture === architecture::x86_64) {
 | 
				
			||||||
 | 
							/**
 | 
				
			||||||
 | 
							 * Due to IEEE 754 double precision format double equality is problematic
 | 
				
			||||||
 | 
							 *
 | 
				
			||||||
 | 
							 * @see https://www.php.net/manual/en/language.types.float.php#113703
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							echo '[' . $action . '][' . ++$test . '][' . (round($record_read_second[0]->motivation, 2) === round(2.2250738585072E-308, 2) ? 'SUCCESS' : 'FAIL') . ']["motivation"] Expected: "2.2250738585072E-308" (double). Actual: "' . $record_read_second[0]->motivation . '" (' . gettype($record_read_second[0]->motivation) . ")\n";
 | 
				
			||||||
 | 
							echo '[' . $action . '][' . ++$test . '][' . ($record_read_second[0]->reputation === (int) 0 ? 'SUCCESS' : 'FAIL') . ']["reputation"] Expected: "0" (integer) (overflow). Actual: "' . $record_read_second[0]->reputation . '" (' . gettype($record_read_second[0]->reputation) . ")\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						echo '[' . $action . "] The read record by filter checks have been completed\n";
 | 
				
			||||||
 | 
					} catch (exception $e) {
 | 
				
			||||||
 | 
						echo '[' . $action . "][WARNING] The read record by filter checks have been completed with errors\n";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reinitializing the counter of tests
 | 
				
			||||||
 | 
					$test = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reading the record from the database by filter with amount limit
 | 
				
			||||||
 | 
					$records_read_filter_amount = $database->read(filter: fn($record) => $record?->age === 24, amount: 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Read the record from the database by filter with amount limit\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
						echo '[' . ++$action . '][' . ++$test . '][' . (gettype($records_read_filter_amount) === 'array' ? 'SUCCESS' : 'FAIL') . '][type of returned value] Expected: "array". Actual: "' . gettype($records_read_filter_amount) . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (count($records_read_filter_amount) === 1 ? 'SUCCESS' : 'FAIL') . '][amount of read records] Expected: 1 records. Actual: ' . count($records_read_filter_amount) . " records\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (gettype($records_read_filter_amount[0]) === 'object' ? 'SUCCESS' : 'FAIL') . '][type of read values] Expected: "object". Actual: "' .  gettype($records_read_filter_amount[0]) . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($records_read_filter_amount[0] instanceof record ? 'SUCCESS' : 'FAIL') . '][class of read object values] Expected: "' . record::class . '". Actual: "' .  $records_read_filter_amount[0]::class . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($records_read_filter_amount[0]->age === 24 ? 'SUCCESS' : 'FAIL') . ']["age"] Expected: "24" (integer). Actual: "' . $records_read_filter_amount[0]->age . '" (' . gettype($records_read_filter_amount[0]->age) . ")\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($records_read_filter_amount[0]->second_name === 'Mirzaev' ? 'SUCCESS' : 'FAIL') . ']["second_name"] Expected: "Mirzaev" (string). Actual: "' . $records_read_filter_amount[0]->second_name . '" (' . gettype($records_read_filter_amount[0]->second_name) . ")\n";
 | 
				
			||||||
 | 
						if ($database->architecture === architecture::x86_64) {
 | 
				
			||||||
 | 
							/**
 | 
				
			||||||
 | 
							 * Due to IEEE 754 double precision format double equality is problematic
 | 
				
			||||||
 | 
							 *
 | 
				
			||||||
 | 
							 * @see https://www.php.net/manual/en/language.types.float.php#113703
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							echo '[' . $action . '][' . ++$test . '][' . (round($record_read_second[0]->motivation, 2) === round(2.2250738585072E-308, 2) ? 'SUCCESS' : 'FAIL') . ']["motivation"] Expected: "2.2250738585072E-308" (double). Actual: "' . $record_read_second[0]->motivation . '" (' . gettype($record_read_second[0]->motivation) . ")\n";
 | 
				
			||||||
 | 
							echo '[' . $action . '][' . ++$test . '][' . ($record_read_second[0]->reputation === (int) 0 ? 'SUCCESS' : 'FAIL') . ']["reputation"] Expected: "0" (integer) (overflow). Actual: "' . $record_read_second[0]->reputation . '" (' . gettype($record_read_second[0]->reputation) . ")\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						echo '[' . $action . "] The read record by filter with amount limit checks have been completed\n";
 | 
				
			||||||
 | 
					} catch (exception $e) {
 | 
				
			||||||
 | 
						echo '[' . $action . "][WARNING] The read record by filter with amount limit checks have been completed with errors\n";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reinitializing the counter of tests
 | 
				
			||||||
 | 
					$test = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reading the record from the database by filter with amount limit and offset
 | 
				
			||||||
 | 
					$records_read_filter_amount_offset = $database->read(filter: fn($record) => $record?->age === 24, amount: 1, offset: 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Read the record from the database by filter with amount limit and offset\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
						echo '[' . ++$action . '][' . ++$test . '][' . (gettype($records_read_filter_amount_offset) === 'array' ? 'SUCCESS' : 'FAIL') . '][type of returned value] Expected: "array". Actual: "' . gettype($records_read_filter_amount_offset) . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (count($records_read_filter_amount_offset) === 1 ? 'SUCCESS' : 'FAIL') . '][amount of read records] Expected: 1 records. Actual: ' . count($records_read_filter_amount_offset) . " records\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (gettype($records_read_filter_amount_offset[0]) === 'object' ? 'SUCCESS' : 'FAIL') . '][type of read values] Expected: "object". Actual: "' .  gettype($records_read_filter_amount_offset[0]) . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($records_read_filter_amount_offset[0] instanceof record ? 'SUCCESS' : 'FAIL') . '][class of read object values] Expected: "' . record::class . '". Actual: "' .  $records_read_filter_amount_offset[0]::class . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($records_read_filter_amount_offset[0]->age === 24 ? 'SUCCESS' : 'FAIL') . ']["age"] Expected: "24" (integer). Actual: "' . $records_read_filter_amount_offset[0]->age . '" (' . gettype($records_read_filter_amount_offset[0]->age) . ")\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($records_read_filter_amount_offset[0]->second_name === 'Ivanov' ? 'SUCCESS' : 'FAIL') . ']["second_name"] Expected: "Ivanov" (string). Actual: "' . $records_read_filter_amount_offset[0]->second_name . '" (' . gettype($records_read_filter_amount_offset[0]->second_name) . ")\n";
 | 
				
			||||||
 | 
						if ($database->architecture === architecture::x86_64) {
 | 
				
			||||||
 | 
							/**
 | 
				
			||||||
 | 
							 * Due to IEEE 754 double precision format double equality is problematic
 | 
				
			||||||
 | 
							 *
 | 
				
			||||||
 | 
							 * @see https://www.php.net/manual/en/language.types.float.php#113703
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							echo '[' . $action . '][' . ++$test . '][' . (round($record_read_second[0]->motivation, 2) === round(2.2250738585072E-308, 2) ? 'SUCCESS' : 'FAIL') . ']["motivation"] Expected: "2.2250738585072E-308" (double). Actual: "' . $record_read_second[0]->motivation . '" (' . gettype($record_read_second[0]->motivation) . ")\n";
 | 
				
			||||||
 | 
							echo '[' . $action . '][' . ++$test . '][' . ($record_read_second[0]->reputation === (int) 0 ? 'SUCCESS' : 'FAIL') . ']["reputation"] Expected: "0" (integer) (overflow). Actual: "' . $record_read_second[0]->reputation . '" (' . gettype($record_read_second[0]->reputation) . ")\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						echo '[' . $action . "] The read record by filter with amount limit and offset checks have been completed\n";
 | 
				
			||||||
 | 
					} catch (exception $e) {
 | 
				
			||||||
 | 
						echo '[' . $action . "][WARNING] The read record by filter with amount limit and offset checks have been completed with errors\n";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reinitializing the counter of tests
 | 
				
			||||||
 | 
					$test = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Deleting the record in the database by filter
 | 
				
			||||||
 | 
					$records_read_filter_delete = $database->read(filter: fn($record) => $record?->name === 'Ivan', delete: true, amount: 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Deleted the record from the database by filter\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reading records from the database after deleting
 | 
				
			||||||
 | 
					$records_read_filter_delete_read = $database->read(amount: 100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Read records from the database after deleting the record\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
						echo '[' . ++$action . '][' . ++$test . '][' . (gettype($records_read_filter_delete) === 'array' ? 'SUCCESS' : 'FAIL') . '][type of returned value] Expected: "array". Actual: "' . gettype($records_read_filter_delete) . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (count($records_read_filter_delete) === 1 ? 'SUCCESS' : 'FAIL') . '][amount of deleted records] Expected: 1 records. Actual: ' . count($records_read_filter_delete) . " records\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (gettype($records_read_filter_delete[0]) === 'object' ? 'SUCCESS' : 'FAIL') . '][type of read values] Expected: "object". Actual: "' .  gettype($records_read_filter_delete[0]) . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($records_read_filter_delete[0] instanceof record ? 'SUCCESS' : 'FAIL') . '][class of read object values] Expected: "' . record::class . '". Actual: "' .  $records_read_filter_delete[0]::class . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($records_read_filter_delete[0]->name === 'Ivan' ? 'SUCCESS' : 'FAIL') . ']["name"] Expected: "Ivan" (string). Actual: "' . $records_read_filter_delete[0]->second_name . '" (' . gettype($records_read_filter_delete[0]->second_name) . ")\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (count($records_read_filter_delete_read) === 2 ? 'SUCCESS' : 'FAIL') . '][amount of read records after deleting] Expected: 2 records. Actual: ' . count($records_read_filter_delete_read) . " records\n";
 | 
				
			||||||
 | 
						if ($database->architecture === architecture::x86_64) {
 | 
				
			||||||
 | 
							/**
 | 
				
			||||||
 | 
							 * Due to IEEE 754 double precision format double equality is problematic
 | 
				
			||||||
 | 
							 *
 | 
				
			||||||
 | 
							 * @see https://www.php.net/manual/en/language.types.float.php#113703
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							echo '[' . $action . '][' . ++$test . '][' . (round($records_read_filter_delete_read[1]->motivation, 2) === round(163, 2) ? 'SUCCESS' : 'FAIL') . ']["motivation"] Expected: "163" (double). Actual: "' . $records_read_filter_delete_read[1]->motivation . '" (' . gettype($records_read_filter_delete_read[1]->motivation) . ")\n";
 | 
				
			||||||
 | 
							echo '[' . $action . '][' . ++$test . '][' . ($record_read_second[0]->reputation === (int) 0 ? 'SUCCESS' : 'FAIL') . ']["reputation"] Expected: "0" (integer) (overflow). Actual: "' . $record_read_second[0]->reputation . '" (' . gettype($record_read_second[0]->reputation) . ")\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						echo '[' . $action . "] The deleted record by filter checks have been completed\n";
 | 
				
			||||||
 | 
					} catch (exception $e) {
 | 
				
			||||||
 | 
						echo '[' . $action . "][WARNING] The deleted record by filter checks have been completed with errors\n";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reinitializing the counter of tests
 | 
				
			||||||
 | 
					$test = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Updating the record in the database
 | 
				
			||||||
 | 
					$records_read_filter_update = $database->read(filter: fn($record) => $record?->name === 'Margarita', update: fn(&$record) => $record->height += 0.5, amount: 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Updated the record in the database by filter\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reading records from the database after updating
 | 
				
			||||||
 | 
					$records_read_filter_update_read = $database->read(amount: 100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo '[' . ++$action . "] Read records from the database after updating the record\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try {
 | 
				
			||||||
 | 
						echo '[' . ++$action . '][' . ++$test . '][' . (gettype($records_read_filter_update) === 'array' ? 'SUCCESS' : 'FAIL') . '][type of returned value] Expected: "array". Actual: "' . gettype($records_read_filter_update) . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (count($records_read_filter_update) === 1 ? 'SUCCESS' : 'FAIL') . '][amount of updated records] Expected: 1 records. Actual: ' . count($records_read_filter_update) . " records\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (gettype($records_read_filter_update[0]) === 'object' ? 'SUCCESS' : 'FAIL') . '][type of read values] Expected: "object". Actual: "' .  gettype($records_read_filter_update[0]) . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($records_read_filter_update[0] instanceof record ? 'SUCCESS' : 'FAIL') . '][class of read object values] Expected: "' . record::class . '". Actual: "' .  $records_read_filter_update[0]::class . "\"\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($records_read_filter_update[0]->height === 165.5 ? 'SUCCESS' : 'FAIL') . ']["height"] Expected: "165.5" (double). Actual: "' . $records_read_filter_update[0]->height . '" (' . gettype($records_read_filter_update[0]->height) . ")\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . (count($records_read_filter_update_read) === 2 ? 'SUCCESS' : 'FAIL') . '][amount of read records after updating] Expected: 2 records. Actual: ' . count($records_read_filter_update_read) . " records\n";
 | 
				
			||||||
 | 
						echo '[' . $action . '][' . ++$test . '][' . ($records_read_filter_update_read[1]->height === $records_read_filter_update[0]->height ? 'SUCCESS' : 'FAIL') . "] Height from `update` process response matched height from the `read` preocess response\n";
 | 
				
			||||||
 | 
						if ($database->architecture === architecture::x86_64) {
 | 
				
			||||||
 | 
							/**
 | 
				
			||||||
 | 
							 * Due to IEEE 754 double precision format double equality is problematic
 | 
				
			||||||
 | 
							 *
 | 
				
			||||||
 | 
							 * @see https://www.php.net/manual/en/language.types.float.php#113703
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							echo '[' . $action . '][' . ++$test . '][' . (round($record_read_second[0]->motivation, 2) === round(2.2250738585072E-308, 2) ? 'SUCCESS' : 'FAIL') . ']["motivation"] Expected: "2.2250738585072E-308" (double). Actual: "' . $record_read_second[0]->motivation . '" (' . gettype($record_read_second[0]->motivation) . ")\n";
 | 
				
			||||||
 | 
							echo '[' . $action . '][' . ++$test . '][' . ($record_read_second[0]->reputation === (int) 0 ? 'SUCCESS' : 'FAIL') . ']["reputation"] Expected: "0" (integer) (overflow). Actual: "' . $record_read_second[0]->reputation . '" (' . gettype($record_read_second[0]->reputation) . ")\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						echo '[' . $action . "] The updated record by filter checks have been completed\n";
 | 
				
			||||||
 | 
					} catch (exception $e) {
 | 
				
			||||||
 | 
						echo '[' . $action . "][WARNING] The updated record by filter checks have been completed with errors\n";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reinitializing the counter of tests
 | 
				
			||||||
 | 
					$test = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo "\n\nCompleted testing";
 | 
				
			||||||
@@ -1,240 +0,0 @@
 | 
				
			|||||||
<?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;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,237 +0,0 @@
 | 
				
			|||||||
<?php
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
declare(strict_types=1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace mirzaev\csv;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Files of the project
 | 
					 | 
				
			||||||
use mirzaev\csv\database;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * CSV
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Comma-Separated Values by RFC 4180
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @see https://tools.ietf.org/html/rfc4180 RFC 4180
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @package mirzaev\csv
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @var array $parameters Parameters of the record
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @method void __construct(string|null $row) Constructor
 | 
					 | 
				
			||||||
 * @method string static serialize() Convert record instance to values for writing into the database
 | 
					 | 
				
			||||||
 * @method void static unserialize(string $row) Convert values from the database and write to the record instance
 | 
					 | 
				
			||||||
 * @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 record
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Parameters
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Mapped with database::COLUMN
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @var array $parameters Parameters of the record
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public protected(set) array $parameters = [];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Constructor
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param string|null $row Row for converting to record instance parameters
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @return void
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public function __construct(?string $row = null)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		// Initializing parameters
 | 
					 | 
				
			||||||
		if (isset($row)) $this->parameters = static::deserialize($row);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Columns
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Combine parameters of the record with columns of the database
 | 
					 | 
				
			||||||
	 * The array of parameters of the record will become associative
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @return static The instance from which the method was called (fluent interface)
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public function columns(database $database): static
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		// Combining database columns with record parameters
 | 
					 | 
				
			||||||
		$this->parameters = array_combine($database->columns, $this->parameters);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Exit (success)
 | 
					 | 
				
			||||||
		return $this;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Serialize
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Convert record instance to values for writing into the database
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @return string Serialized record
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public function serialize(): string
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		// Declaring the buffer of generated row
 | 
					 | 
				
			||||||
		$serialized = '';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		foreach ($this->parameters as $value) {
 | 
					 | 
				
			||||||
			// Iterating over parameters
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// Generating row by RFC 4180
 | 
					 | 
				
			||||||
			$serialized .= ',' . preg_replace('/(?<=[^^])"(?=[^$])/', '""', preg_replace('/(?<=[^^]),(?=[^$])/', '\,', $value ?? ''));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Trimming excess first comma in the buffer of generated row
 | 
					 | 
				
			||||||
		$serialized = mb_substr($serialized, 1, mb_strlen($serialized));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Exit (success)
 | 
					 | 
				
			||||||
		return $serialized;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Deserialize
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Convert values from the database and write to the record instance
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param string $row Row from the database
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @return array Deserialized record
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public function deserialize(string $row): array
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		// Separating row by commas
 | 
					 | 
				
			||||||
		preg_match_all('/(.*)(?>(?<!\\\),|$)/Uu', $row, $matches);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Deleting the last matched element (i could not come up with a better regular expression)
 | 
					 | 
				
			||||||
		array_pop($matches[1]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Generating parameters by RFC 4180
 | 
					 | 
				
			||||||
		foreach ($matches[1] as &$match) {
 | 
					 | 
				
			||||||
			// Iterating over values
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// Declaring buffer of the implementated value
 | 
					 | 
				
			||||||
			$buffer = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if ($match === 'null' || empty($match)) {
 | 
					 | 
				
			||||||
				// Null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Writing to the matches buffer
 | 
					 | 
				
			||||||
				$match = null;
 | 
					 | 
				
			||||||
			} else if (($buffer = filter_var($match, FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE)) !== null) {
 | 
					 | 
				
			||||||
				// Boolean
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Writing to the matches buffer
 | 
					 | 
				
			||||||
				$match = $buffer;
 | 
					 | 
				
			||||||
			} else if (($buffer = filter_var($match, FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE)) !== null) {
 | 
					 | 
				
			||||||
				// Integer
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Writing to the matches buffer
 | 
					 | 
				
			||||||
				$match = $buffer;
 | 
					 | 
				
			||||||
			} else if (($buffer = filter_var($match, FILTER_VALIDATE_FLOAT, FILTER_NULL_ON_FAILURE)) !== null) {
 | 
					 | 
				
			||||||
				// Float
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Writing to the matches buffer
 | 
					 | 
				
			||||||
				$match = $buffer;
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				// String
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Deinitializing unnecessary variables
 | 
					 | 
				
			||||||
				unset($buffer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Removing quotes from both sides (trim() is not suitable here)
 | 
					 | 
				
			||||||
				$unquoted = preg_replace('/"(.*)"/u', '$1', $match);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Unescaping commas
 | 
					 | 
				
			||||||
				$commaded = preg_replace('/\\\,/', ',', $unquoted);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Unescaping quotes (by RFC 4180)
 | 
					 | 
				
			||||||
				$quoted = preg_replace('/""/', '"', $commaded);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Removing line break characters
 | 
					 | 
				
			||||||
				/* $unbreaked =  preg_replace('/[\n\r]/', '', $quoted); */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Removing spaces from both sides
 | 
					 | 
				
			||||||
				/* $unspaced = trim($unbreaked); */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Writing to the matches buffer
 | 
					 | 
				
			||||||
				$match = $quoted;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// Deinitializing unnecessary variables
 | 
					 | 
				
			||||||
			unset($buffer);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Exit (success)
 | 
					 | 
				
			||||||
		return $matches[1];
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Write
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Write the parameter
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param string $name Name of the parameter
 | 
					 | 
				
			||||||
	 * @param mixed $value Content of the parameter
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @return void
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public function __set(string $name, mixed $value = null): void
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		// Writing the parameter and exit (success)
 | 
					 | 
				
			||||||
		$this->parameters[$name] = $value;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Read
 | 
					 | 
				
			||||||
	 * 
 | 
					 | 
				
			||||||
	 * Read the parameter
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param string $name Name of the parameter
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @return mixed Content of the parameter
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public function __get(string $name): mixed
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		// Reading the parameter and exit (success)
 | 
					 | 
				
			||||||
		return $this->parameters[$name];
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Delete
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Delete the parameter
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param string $name Name of the parameter
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @return void
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public function __unset(string $name): void
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		// Deleting the parameter and exit (success)
 | 
					 | 
				
			||||||
		unset($this->parameter[$name]);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Check for initializing
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Check for initializing the parameter
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param string $name Name of the parameter
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @return bool Is the parameter initialized?
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public function __isset(string $name): bool
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		// Checking for initializing the parameter and exit (success)
 | 
					 | 
				
			||||||
		return isset($this->parameters[$name]);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,103 +0,0 @@
 | 
				
			|||||||
<?php
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
declare(strict_types=1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace mirzaev\csv\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 $rows Amount of rows for reading
 | 
					 | 
				
			||||||
	 * @param int $offset Offset of rows for start 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 $rows = 10,  int $offset = 0, 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;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,92 +0,0 @@
 | 
				
			|||||||
<?php
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use mirzaev\csv\database,
 | 
					 | 
				
			||||||
	mirzaev\csv\record;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Importing files of thr project and dependencies
 | 
					 | 
				
			||||||
require(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Initializing the counter of actions
 | 
					 | 
				
			||||||
$action = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Initializing the test database
 | 
					 | 
				
			||||||
$database = new database([
 | 
					 | 
				
			||||||
	'empty_value_at_the_beginning',
 | 
					 | 
				
			||||||
	'name',
 | 
					 | 
				
			||||||
	'second_name',
 | 
					 | 
				
			||||||
	'age_between_quotes',
 | 
					 | 
				
			||||||
	'true',
 | 
					 | 
				
			||||||
	'empty_value',
 | 
					 | 
				
			||||||
	'empty_value_between_quotes',
 | 
					 | 
				
			||||||
	'number',
 | 
					 | 
				
			||||||
	'null',
 | 
					 | 
				
			||||||
	'null_between_quotes',
 | 
					 | 
				
			||||||
	'float_between_quotes',
 | 
					 | 
				
			||||||
	'float',
 | 
					 | 
				
			||||||
	'float_long',
 | 
					 | 
				
			||||||
	'float_with_two_dots',
 | 
					 | 
				
			||||||
	'string_with_doubled_quotes',
 | 
					 | 
				
			||||||
	'string_with_doubled_quotes_twice',
 | 
					 | 
				
			||||||
	'string_with_space_at_the_beginning',
 | 
					 | 
				
			||||||
	'string_with_escaped_comma',
 | 
					 | 
				
			||||||
	'string_with_unicode_symbols'
 | 
					 | 
				
			||||||
]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo '[' . ++$action . "] Created the database instance with columns\n";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Initializing the test record with the test row
 | 
					 | 
				
			||||||
$record = new record(',"Arsen","Mirzaev","23",true,,"",100,null,"null","102.1",300.34,1001.23145,5000.400.400,"test ""value""","another"" test "" value with ""two double quotes pairs"" yeah"," starts with space","has\, an escaped comma inside","unicode символы"');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo '[' . ++$action . "] Created the record with the test row\n";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Initializing the counter of tests
 | 
					 | 
				
			||||||
$test = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[0] === null ? 'SUCCESS' : 'FAIL') . "] The empty value at the beginning is need to be null\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[1] === 'Arsen' ? 'SUCCESS' : 'FAIL') . "] The value is need to be \"Arsen\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[2] === 'Mirzaev' ? 'SUCCESS' : 'FAIL') . "] The value is need to be \"Mirzaev\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[3] === '23' ? 'SUCCESS' : 'FAIL') . "] The age between quotes value is need to be \"23\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[4] === true ? 'SUCCESS' : 'FAIL') . "] The value is need to be true\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[5] === null ? 'SUCCESS' : 'FAIL') . "] The empty value is need to be null\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[6] === '' ? 'SUCCESS' : 'FAIL') . "] The empty value between quotes is need to be \"\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[7] === 100 ? 'SUCCESS' : 'FAIL') . "] The value is need to be 100 integer\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[8] === null ? 'SUCCESS' : 'FAIL') . "] The null value is need to be null\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[9] === 'null' ? 'SUCCESS' : 'FAIL') . "] The null value between quotes is need to be \"null\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[10] === '102.1' ? 'SUCCESS' : 'FAIL') . "] The float value between quotes is need to be \"102.1\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[11] === 300.34 ? 'SUCCESS' : 'FAIL') . "] The float value is need to be 300.34 float \n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[12] === 1001.23145 ? 'SUCCESS' : 'FAIL') . "] The long float value is need to be 1001.23145 float\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[13] === '5000.400.400' ? 'SUCCESS' : 'FAIL') . "] The float value with two dots is need to be \"5000.400.400\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[14] === 'test "value"' ? 'SUCCESS' : 'FAIL') . "] The value with quotes is need to be \"test \"value\"\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[15] === 'another" test " value with "two double quotes pairs" yeah' ? 'SUCCESS' : 'FAIL') . "] The value is need to be \"another\" test \" value with \"two double quotes pairs\" yeah\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[16] === ' starts with space' ? 'SUCCESS' : 'FAIL') . "] The value is need to be \" starts with space\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[17] === 'has, an escaped comma inside' ? 'SUCCESS' : 'FAIL') . "] The value is need to be \"has, an escaped comma inside\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->parameters[18] === 'unicode символы' ? 'SUCCESS' : 'FAIL') . "] The valueis need to be \"unicode символы\" string\n";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Combining database columns with  record parameters
 | 
					 | 
				
			||||||
$record->columns($database);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo '[' . ++$action . "] Combined database columns with record parameters\n";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Reinitializing the counter of tests
 | 
					 | 
				
			||||||
$test = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->empty_value_at_the_beginning === null ? 'SUCCESS' : 'FAIL') . "] The empty value at the beginning is need to be null\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->name === 'Arsen' ? 'SUCCESS' : 'FAIL') . "] The value is need to be \"Arsen\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->second_name === 'Mirzaev' ? 'SUCCESS' : 'FAIL') . "] The value is need to be \"Mirzaev\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->age_between_quotes === '23' ? 'SUCCESS' : 'FAIL') . "] The age between quotes value is need to be \"23\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->true === true ? 'SUCCESS' : 'FAIL') . "] The value is need to be true\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->empty_value === null ? 'SUCCESS' : 'FAIL') . "] The empty value is need to be null\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->empty_value_between_quotes === '' ? 'SUCCESS' : 'FAIL') . "] The empty value between quotes is need to be \"\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->number === 100 ? 'SUCCESS' : 'FAIL') . "] The value is need to be 100 integer\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->null === null ? 'SUCCESS' : 'FAIL') . "] The null value is need to be null\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->null_between_quotes === 'null' ? 'SUCCESS' : 'FAIL') . "] The null value between quotes is need to be \"null\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->float_between_quotes === '102.1' ? 'SUCCESS' : 'FAIL') . "] The float value between quotes is need to be \"102.1\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->float === 300.34 ? 'SUCCESS' : 'FAIL') . "] The float value is need to be 300.34 float \n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->float_long === 1001.23145 ? 'SUCCESS' : 'FAIL') . "] The long float value is need to be 1001.23145 float\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->float_with_two_dots === '5000.400.400' ? 'SUCCESS' : 'FAIL') . "] The float value with two dots is need to be \"5000.400.400\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->string_with_doubled_quotes === 'test "value"' ? 'SUCCESS' : 'FAIL') . "] The value with quotes is need to be \"test \"value\"\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->string_with_doubled_quotes_twice === 'another" test " value with "two double quotes pairs" yeah' ? 'SUCCESS' : 'FAIL') . "] The value is need to be \"another\" test \" value with \"two double quotes pairs\" yeah\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->string_with_space_at_the_beginning === ' starts with space' ? 'SUCCESS' : 'FAIL') . "] The value is need to be \" starts with space\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->string_with_escaped_comma === 'has, an escaped comma inside' ? 'SUCCESS' : 'FAIL') . "] The value is need to be \"has, an escaped comma inside\" string\n";
 | 
					 | 
				
			||||||
echo '[' . ++$action . '][' . ++$test . '][' . ($record->string_with_unicode_symbols === 'unicode символы' ? 'SUCCESS' : 'FAIL') . "] The valueis need to be \"unicode символы\" string\n";
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user