From 0843fd83a54b222e86584990279ab6074478cf01 Mon Sep 17 00:00:00 2001 From: Arsen Mirzaev Tatyano-Muradovich Date: Sun, 15 Dec 2024 00:20:44 +0300 Subject: [PATCH] PHP 8.4 + MINIMAL 3.2.0 + many improvements --- .gitea/template | 0 .gitignore | 0 author/project/system/controllers/core.php | 86 ++--- author/project/system/controllers/index.php | 53 ++- author/project/system/models/core.php | 201 ++++-------- .../system/models/enumerations/language.php | 50 +++ .../system/models/enumerations/session.php | 21 ++ .../system/models/interfaces/collection.php | 31 ++ .../system/models/interfaces/document.php | 81 +++++ author/project/system/models/session.php | 307 ++++++++---------- .../project/system/models/traits/buffer.php | 84 +++++ .../project/system/models/traits/document.php | 206 ++++++++++++ author/project/system/models/traits/files.php | 66 ++++ .../project/system/models/traits/status.php | 24 +- .../system/public/css/themes/default/main.css | 1 - author/project/system/public/index.php | 39 ++- author/project/system/settings/.gitignore | 0 .../system/settings/arangodb.php.sample | 0 author/project/system/views/templater.php | 77 +++-- .../system/views/themes/default/core.html | 0 .../system/views/themes/default/footer.html | 0 .../system/views/themes/default/head.html | 0 .../system/views/themes/default/header.html | 0 .../system/views/themes/default/index.html | 0 .../system/views/themes/default/js.html | 0 composer.json | 20 +- 26 files changed, 931 insertions(+), 416 deletions(-) mode change 100644 => 100755 .gitea/template mode change 100644 => 100755 .gitignore mode change 100644 => 100755 author/project/system/controllers/core.php mode change 100644 => 100755 author/project/system/controllers/index.php mode change 100644 => 100755 author/project/system/models/core.php create mode 100755 author/project/system/models/enumerations/language.php create mode 100755 author/project/system/models/enumerations/session.php create mode 100755 author/project/system/models/interfaces/collection.php create mode 100755 author/project/system/models/interfaces/document.php mode change 100644 => 100755 author/project/system/models/session.php create mode 100755 author/project/system/models/traits/buffer.php create mode 100755 author/project/system/models/traits/document.php create mode 100755 author/project/system/models/traits/files.php mode change 100644 => 100755 author/project/system/models/traits/status.php mode change 100644 => 100755 author/project/system/public/css/themes/default/main.css mode change 100644 => 100755 author/project/system/public/index.php mode change 100644 => 100755 author/project/system/settings/.gitignore mode change 100644 => 100755 author/project/system/settings/arangodb.php.sample mode change 100644 => 100755 author/project/system/views/templater.php mode change 100644 => 100755 author/project/system/views/themes/default/core.html mode change 100644 => 100755 author/project/system/views/themes/default/footer.html mode change 100644 => 100755 author/project/system/views/themes/default/head.html mode change 100644 => 100755 author/project/system/views/themes/default/header.html mode change 100644 => 100755 author/project/system/views/themes/default/index.html mode change 100644 => 100755 author/project/system/views/themes/default/js.html diff --git a/.gitea/template b/.gitea/template old mode 100644 new mode 100755 diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/author/project/system/controllers/core.php b/author/project/system/controllers/core.php old mode 100644 new mode 100755 index 6407bfb..5d331a7 --- a/author/project/system/controllers/core.php +++ b/author/project/system/controllers/core.php @@ -7,40 +7,61 @@ namespace ${REPO_OWNER}\${REPO_NAME}\controllers; // Files of the project use ${REPO_OWNER}\${REPO_NAME}\views\manager, ${REPO_OWNER}\${REPO_NAME}\models\core as models, - ${REPO_OWNER}\${REPO_NAME}\models\account_model as account, - ${REPO_OWNER}\${REPO_NAME}\models\session_model as session; - -// Library for ArangoDB -use ArangoDBClient\Document as _document; + ${REPO_OWNER}\${REPO_NAME}\models\session; // Framework for PHP -use mirzaev\minimal\controller; +use mirzaev\minimal\core as minimal, + mirzaev\minimal\controller, + mirzaev\minimal\http\response, + mirzaev\minimal\http\enumerations\status; /** - * Core of controllers + * Controllers core * * @package ${REPO_OWNER}\${REPO_NAME}\controllers - * @author ${REPO_OWNER} < mail > + * + * @param session $session Instance of the session + * @param language $language Language + * @param response $response Response + * @param array $errors Registry of errors + * + * @method void __construct(minimal $minimal, bool $initialize) Constructor + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author ${REPO_OWNER} */ class core extends controller { /** - * Postfix for name of controllers files - */ - final public const POSTFIX = ''; - - /** - * Instance of a session + * Session + * + * @var session|null $session Instance of the session */ protected readonly session $session; /** - * Instance of an account + * Language + * + * @var language $language Language */ - protected readonly ?account $account; + protected language $language = language::en; /** - * Registry of errors + * Response + * + * @see https://wiki.php.net/rfc/property-hooks (find a table about backed and virtual hooks) + * + * @var response $response Response + */ + protected response $response { + // Read + get => $this->response ??= $this->request->response(); + } + + /** + * Errors + * + * @var array $errors Registry of errors */ protected array $errors = [ 'session' => [], @@ -48,24 +69,25 @@ class core extends controller ]; /** - * Constructor of an instance + * Constructor * + * @param minimal $minimal Instance of the MINIMAL * @param bool $initialize Initialize a controller? * * @return void */ - public function __construct(bool $initialize = true) + public function __construct(minimal $minimal, bool $initialize = true) { // Blocking requests from CloudFlare (better to write this blocking into nginx config file) - if ($_SERVER['HTTP_USER_AGENT'] === 'nginx-ssl early hints') return; + if (isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT'] === 'nginx-ssl early hints') return status::bruh->label; // For the extends system - parent::__construct($initialize); + parent::__construct(core: $minimal); if ($initialize) { - // Initializing is requested + // Requestet initializing - // Initializing of models core (connect to ArangoDB...) + // Initializing core of the models new models(); // Initializing of the date until which the session will be active @@ -100,22 +122,4 @@ class core extends controller $this->view = new templater($this->session); } } - - /** - * Check of initialization - * - * Checks whether a property is initialized in a document instance from ArangoDB - * - * @param string $name Name of the property from ArangoDB - * - * @return bool The property is initialized? - */ - public function __isset(string $name): bool - { - // Check of initialization of the property and exit (success) - return match ($name) { - default => isset($this->{$name}) - }; - } - } diff --git a/author/project/system/controllers/index.php b/author/project/system/controllers/index.php old mode 100644 new mode 100755 index a19a1c4..4fc0fcb --- a/author/project/system/controllers/index.php +++ b/author/project/system/controllers/index.php @@ -7,24 +7,61 @@ namespace ${REPO_OWNER}\${REPO_NAME}\controllers; // Files of the project use ${REPO_OWNER}\${REPO_NAME}\controllers\core; +// Framework for PHP +use mirzaev\minimal\http\enumerations\status; + /** - * Index controller + * Index * * @package ${REPO_OWNER}\${REPO_NAME}\controllers - * @author ${REPO_OWNER} < mail > + * + * @param array $errors Registry of errors + * + * @method null index() Main page + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author ${REPO_OWNER} */ final class index extends core { /** - * Render the main page + * Errors * - * @param array $parameters Parameters of the request (POST + GET) + * @var array $errors Registry of errors */ - public function index(array $parameters = []): ?string + protected array $errors = [ + 'session' => [] + ]; + + /** + * Main page + * + * @return null + */ + public function index(): null { - // Exit (success) - if ($_SERVER['REQUEST_METHOD'] === 'GET') return $this->view->render(DIRECTORY_SEPARATOR . 'index.html'); - else if ($_SERVER['REQUEST_METHOD'] === 'POST') return $main; + if (str_contains($this->request->headers['accept'], content::any->value)) { + // Request for any response + + // Render page + $page = $this->view->render('index.html'); + + // Sending response + $this->response + ->start() + ->clean() + ->sse() + ->write($page) + ->validate($this->request) + ?->body() + ->end(); + + // Deinitializing rendered page + unset($page); + + // Exit (success) + return null; + } // Exit (fail) return null; diff --git a/author/project/system/models/core.php b/author/project/system/models/core.php old mode 100644 new mode 100755 index 28186bc..49dd7f1 --- a/author/project/system/models/core.php +++ b/author/project/system/models/core.php @@ -5,14 +5,15 @@ declare(strict_types=1); namespace ${REPO_OWNER}\${REPO_NAME}\models; // Framework for PHP -use mirzaev\minimal\model; +use mirzaev\minimal\model, + mirzaev\minimal\http\enumerations\status; // Framework for ArangoDB use mirzaev\arangodb\connection as arangodb, mirzaev\arangodb\collection, mirzaev\arangodb\document; -// Libraries for ArangoDB +// Library for ArangoDB use ArangoDBClient\Document as _document, ArangoDBClient\DocumentHandler as _document_handler; @@ -20,38 +21,40 @@ use ArangoDBClient\Document as _document, use exception; /** - * Core of models + * Models core * - * @package ${REPO_OWNER}\${REPO_NAME}\controllers - * @author ${REPO_OWNER} < mail > + * @package ${REPO_OWNER}\${REPO_NAME}\models + * + * @param public ARANGODB Path to the file with ArangoDB session connection data + * @param arangodb $arangodb Instance of the ArangoDB session + * + * @method void __construct(bool $initialize, ?arangodb $arangodb) Constructor + * @method _document|static|array|null read(string $filter, string $sort, int $amount, int $page, string $return, array $parameters, array &$errors) Read document from ArangoDB + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author ${REPO_OWNER} */ class core extends model { /** - * Postfix for name of models files + * ArangoDB connection daa + * + * @var string ARANGODB Path to the file with ArangoDB session connection data */ - final public const POSTFIX = ''; + final public const string ARANGODB = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'settings' . DIRECTORY_SEPARATOR . 'arangodb.php'; /** - * Path to the file with settings of connecting to the ArangoDB - */ - final public const ARANGODB = '../settings/arangodb.php'; - - /** - * Instance of the session of ArangoDB + * ArangoDB + * + * @var arangodb $arangodb Instance of the ArangoDB session */ protected static arangodb $arangodb; /** - * Name of the collection in ArangoDB - */ - public const COLLECTION = 'THIS_COLLECTION_SHOULD_NOT_EXIST_REPLACE_IT_IN_THE_MODEL'; - - /** - * Constructor of an instance + * Constructor * * @param bool $initialize Initialize a model? - * @param ?arangodb $arangodb Instance of a session of ArangoDB + * @param ?arangodb $arangodb Instance of the ArangoDB session * * @return void */ @@ -63,66 +66,81 @@ class core extends model if ($initialize) { // Initializing is requested - if (isset($arangodb)) { - // Recieved an instance of a session of ArangoDB - - // Write an instance of a session of ArangoDB to the property - $this->__set('arangodb', $arangodb); - } else { - // Not recieved an instance of a session of ArangoDB - - // Initializing of an instance of a session of ArangoDB - $this->__get('arangodb'); - } + // Writing an instance of a session of ArangoDB to the property + self::$arangodb = $arangodb ?? new arangodb(require static::ARANGODB); } } /** - * Read from ArangoDB + * Read document from ArangoDB * * @param string $filter Expression for filtering (AQL) * @param string $sort Expression for sorting (AQL) * @param int $amount Amount of documents for collect * @param int $page Page * @param string $return Expression describing the parameters to return (AQL) - * @param array &$errors The registry on errors + * @param array $parameters Binded parameters for placeholders ['placeholder' => parameter] + * @param array &$errors Registry of errors * - * @return _document|array|null An array of instances of documents from ArangoDB, if they are found + * @return mixed An array of instances of documents from ArangoDB, if they are found */ - public static function read( + public static function _read( string $filter = '', string $sort = 'd.created DESC, d._key DESC', int $amount = 1, int $page = 1, string $return = 'd', + array $parameters = [], array &$errors = [] - ): _document|array|null { + ): _document|static|array|null { try { - if (collection::init(static::$arangodb->session, static::COLLECTION)) { + if (collection::initialize(static::COLLECTION, static::TYPE)) { // Initialized the collection - // Read from ArangoDB and exit (success) - return collection::search( - static::$arangodb->session, + // Read from ArangoDB + $result = collection::execute( sprintf( <<<'AQL' - FOR d IN %s + FOR d IN @@collection %s %s - LIMIT %d, %d + LIMIT @offset, @amount RETURN %s AQL, - static::COLLECTION, empty($filter) ? '' : "FILTER $filter", empty($sort) ? '' : "SORT $sort", - --$page <= 0 ? 0 : $amount * $page, - $amount, - $return - ) + empty($return) ? 'd' : $return + ), + [ + '@collection' => static::COLLECTION, + 'offset' => --$page <= 0 ? 0 : $page * $amount, + 'amount' => $amount + ] + $parameters, + errors: $errors ); - } else throw new exception('Failed to initialize the collection'); + + if ($amount === 1 && $result instanceof _document) { + // Received only 1 document and @todo rebuild + + // Initializing the object + $object = new static; + + if (method_exists($object, '__document')) { + // Object can implement a document from ArangoDB + + // Writing the instance of document from ArangoDB to the implement object + $object->__document($result); + + // Exit (success) + return $object; + } + } + + // Exit (success) + return $result; + } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } catch (exception $e) { - // Write to the registry of errors + // Writing to registry of errors $errors[] = [ 'text' => $e->getMessage(), 'file' => $e->getFile(), @@ -135,50 +153,6 @@ class core extends model return null; } - /** - * Delete from ArangoDB - * - * @param _document $instance Instance of the document from ArangoDB - * @param array &$errors The registry on errors - * - * @return bool Deleted from ArangoDB without errors? - */ - public static function delete(_document $instance, array &$errors = []): bool - { - try { - if (collection::init(static::$arangodb->session, static::COLLECTION)) { - // Initialized the collection - - // Delete from ArangoDB and exit (success) - return (new _document_handler(static::$arangodb->session))->remove($instance); - } else throw new exception('Failed to initialize the collection'); - } catch (exception $e) { - // Write to the registry of errors - $errors[] = [ - 'text' => $e->getMessage(), - 'file' => $e->getFile(), - 'line' => $e->getLine(), - 'stack' => $e->getTrace() - ]; - } - - // Exit (fail) - return false; - } - - /** - * Update in ArangoDB - * - * @param _document $instance Instance of the document from ArangoDB - * - * @return bool Writed to ArangoDB without errors? - */ - public static function update(_document $instance): bool - { - // Update in ArangoDB and exit (success) - return document::update(static::$arangodb->session, $instance); - } - /** * Write * @@ -191,26 +165,9 @@ class core extends model { match ($name) { 'arangodb' => (function () use ($value) { - if ($this->__isset('arangodb')) { - // Is alredy initialized - - // Exit (fail) - throw new exception('Forbidden to reinitialize the session of ArangoDB ($this::$arangodb)', 500); - } else { - // Is not already initialized - - if ($value instanceof arangodb) { - // Recieved an appropriate value - - // Write the property and exit (success) - self::$arangodb = $value; - } else { - // Recieved an inappropriate value - - // Exit (fail) - throw new exception('Session of ArangoDB ($this::$arangodb) is need to be mirzaev\arangodb\connection', 500); - } - } + if (isset(static::$arangodb)) throw new exception('Forbidden to reinitialize the ArangoDB session($this::$arangodb)', status::internal_server_error->value); + else if ($value instanceof arangodb) self::$arangodb = $value; + else throw new exception('Session of connection to ArangoDB ($this::$arangodb) is need to be mirzaev\arangodb\connection', status::internal_server_error->value); })(), default => parent::__set($name, $value) }; @@ -226,22 +183,6 @@ class core extends model public function __get(string $name): mixed { return match ($name) { - 'arangodb' => (function () { - try { - if (!$this->__isset('arangodb')) { - // Is not initialized - - // Initializing of a default value from settings - $this->__set('arangodb', new arangodb(require static::ARANGODB)); - } - - // Exit (success) - return self::$arangodb; - } catch (exception) { - // Exit (fail) - return null; - } - })(), default => parent::__get($name) }; } @@ -280,7 +221,7 @@ class core extends model */ public static function __callStatic(string $name, array $arguments): mixed { - match ($name) { + return match ($name) { 'arangodb' => (new static)->__get('arangodb'), default => throw new exception("Not found: $name", 500) }; diff --git a/author/project/system/models/enumerations/language.php b/author/project/system/models/enumerations/language.php new file mode 100755 index 0000000..5fc98a8 --- /dev/null +++ b/author/project/system/models/enumerations/language.php @@ -0,0 +1,50 @@ + + * @author ${REPO_OWNER} + */ +enum language +{ + case en; + case ru; + + /** + * Label + * + * Initialize label of the language + * + * @param language|null $language Language into which to translate + * + * @return string Translated label of the language + * + * @todo + * 1. More languages + * 2. Cases??? + */ + public function label(?language $language = language::en): string + { + // Exit (success) + return match ($this) { + language::en => match ($language) { + language::en => 'English', + language::ru => 'Английский' + }, + language::ru => match ($language) { + language::en => 'Russian', + language::ru => 'Русский' + } + }; + } +} diff --git a/author/project/system/models/enumerations/session.php b/author/project/system/models/enumerations/session.php new file mode 100755 index 0000000..6163124 --- /dev/null +++ b/author/project/system/models/enumerations/session.php @@ -0,0 +1,21 @@ + + */ +enum session +{ + case hash_only; + case hash_else_address; +} diff --git a/author/project/system/models/interfaces/collection.php b/author/project/system/models/interfaces/collection.php new file mode 100755 index 0000000..95db7e8 --- /dev/null +++ b/author/project/system/models/interfaces/collection.php @@ -0,0 +1,31 @@ + + */ +interface collection +{ + /** + * Name of the collection in ArangoDB + */ + public const string COLLECTION = 'THIS_COLLECTION_SHOULD_NOT_EXIST'; + + /** + * Type of the collection in ArangoDB + */ + public const type TYPE = type::document; +} diff --git a/author/project/system/models/interfaces/document.php b/author/project/system/models/interfaces/document.php new file mode 100755 index 0000000..013673c --- /dev/null +++ b/author/project/system/models/interfaces/document.php @@ -0,0 +1,81 @@ + + */ +interface document +{ + /** + * Write + * + * Write a property into an instance of the ArangoDB document + * + * @param string $name Name of the property + * @param mixed $value Content of the property + * + * @return void + */ + public function __set(string $name, mixed $value = null): void; + + /** + * Read + * + * Read a property from an instance of the ArangoDB docuemnt + * + * @param string $name Name of the property + * + * @return mixed Content of the property + */ + public function __get(string $name): mixed; + + + /** + * Delete + * + * Deinitialize the property in an instance of the ArangoDB document + * + * @param string $name Name of the property + * + * @return void + */ + public function __unset(string $name): void; + + /** + * Check of initialization + * + * Check of initialization of the property into an instance of the ArangoDB document + * + * @param string $name Name of the property + * + * @return bool The property is initialized? + */ + public function __isset(string $name): bool; + + /** + * Execute a method + * + * Execute a method from an instance of the ArangoDB document + * + * @param string $name Name of the method + * @param array $arguments Arguments for the method + * + * @return mixed Result of execution of the method + */ + public function __call(string $name, array $arguments = []): mixed; +} diff --git a/author/project/system/models/session.php b/author/project/system/models/session.php old mode 100644 new mode 100755 index 3c7ff37..715c4ae --- a/author/project/system/models/session.php +++ b/author/project/system/models/session.php @@ -5,8 +5,13 @@ declare(strict_types=1); namespace ${REPO_OWNER}\${REPO_NAME}\models; // Files of the project -use mirzaev\ebala\models\account, - mirzaev\ebala\models\traits\status; +use ${REPO_OWNER}\${REPO_NAME}\models\traits\status, + ${REPO_OWNER}\${REPO_NAME}\models\traits\buffer, + ${REPO_OWNER}\${REPO_NAME}\models\traits\document as document_trait, + ${REPO_OWNER}\${REPO_NAME}\models\interfaces\document as document_interface, + ${REPO_OWNER}\${REPO_NAME}\models\interfaces\collection as collection_interface, + ${REPO_OWNER}\${REPO_NAME}\models\enumerations\session as verification, + ${REPO_OWNER}\${REPO_NAME}\models\enumerations\language; // Framework for ArangoDB use mirzaev\arangodb\collection, @@ -19,81 +24,113 @@ use ArangoDBClient\Document as _document; use exception; /** - * Model of session + * Session model * - * @package ${REPO_OWNER}\${REPO_NAME}\controllers - * @author ${REPO_OWNER} < mail > + * @package ${REPO_OWNER}\${REPO_NAME}\models + * + * @param string COLLECTION Name of the collection in ArangoDB + * @param verification VERIFICATION Type of session verification + * + * @method void __construct(?string $hash, ?int $expires, array &$errors) Constructor + * @method document|null hash(string $hash, array &$errors) Search by hash + * @method document|null address(string $address, array &$errors) Search by IP-address + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author ${REPO_OWNER} */ -final class session extends core +final class session extends core implements document_interface, collection_interface { - /** - * Name of the collection in ArangoDB - */ - final public const COLLECTION = 'session'; + use status, document_trait, buffer, cart { + buffer::write as write; + cart::initialize as cart; + } /** - * An instance of the ArangoDB document from ArangoDB + * Collection name + * + * @var string COLLECTION Name of the collection in ArangoDB */ - protected readonly _document $document; + final public const string COLLECTION = 'session'; /** - * Constructor of an instance + * Session verification type + * + * @var verification VERIFICATION Type of session verification + */ + final public const verification VERIFICATION = verification::hash_else_address; + + /** + * Constructor * - * Initialize of a session and write them to the $this->document property + * Initialize session and write into the $this->document property * * @param ?string $hash Hash of the session in ArangoDB * @param ?int $expires Date of expiring of the session (used for creating a new session) * @param array &$errors Registry of errors * - * @return static instance of the ArangoDB document of session + * @return void */ public function __construct(?string $hash = null, ?int $expires = null, array &$errors = []) { try { - if (collection::init(static::$arangodb->session, self::COLLECTION)) { + if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) { // Initialized the collection - if ($this->search($hash, $errors)) { - // Found an instance of the ArangoDB document of session and received a session hash + if (isset($hash) && $document = $this->hash($hash, errors: $errors)) { + // Found the instance of the ArangoDB document of session and received a session hash + + // Writing document instance of the session from ArangoDB to the property of the implementing object + $this->__document($document); + } else if (static::VERIFICATION === verification::hash_else_address && $document = $this->address($_SERVER['REMOTE_ADDR'], errors: $errors)) { + // Found the instance of the ArangoDB document of session and received a session hash + + // Writing document instance of the session from ArangoDB to the property of the implementing object + $this->__document($document); } else { - // Not found an instance of the ArangoDB document of session + // Not found the instance of the ArangoDB document of session // Initializing a new session and write they into ArangoDB - $_id = document::write($this::$arangodb->session, self::COLLECTION, [ - 'active' => true, - 'expires' => $expires ?? time() + 604800, - 'ip' => $_SERVER['REMOTE_ADDR'], - 'x-forwarded-for' => $_SERVER['HTTP_X_FORWARDED_FOR'] ?? null, - 'referer' => $_SERVER['HTTP_REFERER'] ?? null, - 'useragent' => $_SERVER['HTTP_USER_AGENT'] ?? null - ]); + $_id = document::write( + static::COLLECTION, + [ + 'active' => true, + 'expires' => $expires ?? time() + 604800, + 'address' => $_SERVER['REMOTE_ADDR'], + 'x-forwarded-for' => $_SERVER['HTTP_X_FORWARDED_FOR'] ?? null, + 'referer' => $_SERVER['HTTP_REFERER'] ?? null, + 'useragent' => $_SERVER['HTTP_USER_AGENT'] ?? null + ] + ); - if ($session = collection::search($this::$arangodb->session, sprintf( - << %d && d.active == true + if ($session = collection::execute( + <<<'AQL' + FOR d IN @@collection + FILTER d._id == @_id && d.expires > @time && d.active == true RETURN d AQL, - self::COLLECTION, - $_id, - time() - ))) { - // Found an instance of just created new session + [ + '@collection' => static::COLLECTION, + '_id' => $_id, + 'time' => time() + ], + errors: $errors + )) { + // Found the instance of just created new session - // Generate a hash and write into an instance of the ArangoDB document of session property + // Generating a hash and write into the instance of the ArangoDB document of session property $session->hash = sodium_bin2hex(sodium_crypto_generichash($_id)); - if (document::update($this::$arangodb->session, $session)) { - // Is writed update + if (document::update($session, errors: $errors)) { + // Writed to ArangoDB - // Write instance of the ArangoDB document of session into property and exit (success) - $this->document = $session; - } else throw new exception('Could not write the session data'); - } else throw new exception('Could not create or find just created session'); + // Writing instance of the session document from ArangoDB to the property of the implementing object + $this->__document($session); + } else throw new exception('Failed to write the session data'); + } else throw new exception('Failed to create or find just created session'); } - } else throw new exception('Could not initialize the collection'); + } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } catch (exception $e) { - // Write to the registry of errors + // Writing to the registry of errors $errors[] = [ 'text' => $e->getMessage(), 'file' => $e->getFile(), @@ -104,45 +141,38 @@ final class session extends core } /** - * Search + * Search by hash * - * Search for the session in ArangoDB by hash and write they into $this->document property if they are found + * Search for the session in ArangoDB by hash * - * @param ?string $hash Hash of the session in ArangoDB + * @param string $hash Hash of the session in ArangoDB * @param array &$errors Registry of errors * - * @return static instance of the ArangoDB document of session + * @return _document|null instance of document of the session in ArangoDB */ - public function search(?string $hash, array &$errors = []): bool + public static function hash(string $hash, array &$errors = []): ?_document { try { - if (isset($hash)) { - // Recieved a hash + if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) { + // Collection initialized // Search the session data in ArangoDB - $_document = $session = collection::search($this::$arangodb->session, sprintf( - << %d && d.active == true + return collection::execute( + <<<'AQL' + FOR d IN @@collection + FILTER d.hash == @hash && d.expires > @time && d.active == true RETURN d - AQL, - self::COLLECTION, - $hash, - time() - )); - - if ($_document instanceof _document) { - // An instance of the ArangoDB document of session is found - - // Write the session data to the property - $this->document = $_document; - - // Exit (success) - return true; - } - } + AQL, + [ + '@collection' => static::COLLECTION, + 'hash' => $hash, + 'time' => time() + ], + errors: $errors + ); + } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } catch (exception $e) { - // Write to the registry of errors + // Writing to the registry of errors $errors[] = [ 'text' => $e->getMessage(), 'file' => $e->getFile(), @@ -152,37 +182,44 @@ final class session extends core } // Exit (fail) - return false; + return null; } /** - * Write to buffer of the session + * Search by IP-address * - * @param array $data Data for merging + * Search for the session in ArangoDB by IP-address + * + * @param string $address IP-address writed to the session in ArangoDB * @param array &$errors Registry of errors * - * @return bool Is data has written into the session buffer? + * @return _document|null instance of document of the session in ArangoDB */ - public function write(array $data, array &$errors = []): bool + public static function address(string $address, array &$errors = []): ?_document { try { - if (collection::init($this::$arangodb->session, self::COLLECTION)) { - // Initialized the collection + if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) { + // Collection initialized - // An instance of the ArangoDB document of session is initialized? - if (!isset($this->document)) throw new exception('An instance of the ArangoDB document of session is not initialized'); - - // Write data into buffwer of an instance of the ArangoDB document of session - $this->document->buffer = array_replace_recursive( - $this->document->buffer ?? [], - [$_SERVER['INTERFACE'] => array_replace_recursive($this->document->buffer[$_SERVER['INTERFACE']] ?? [], $data)] + // Search the session data in ArangoDB + return collection::execute( + <<<'AQL' + FOR d IN @@collection + FILTER d.address == @address && d.expires > @time && d.active == true + SORT d.updated DESC + LIMIT 1 + RETURN d + AQL, + [ + '@collection' => static::COLLECTION, + 'address' => $address, + 'time' => time() + ], + errors: $errors ); - - // Write to ArangoDB and exit (success) - return document::update($this::$arangodb->session, $this->document) ? true : throw new exception('Не удалось записать данные в буфер сессии'); - } else throw new exception('Could not initialize the collection'); + } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } catch (exception $e) { - // Write to the registry of errors + // Writing to the registry of errors $errors[] = [ 'text' => $e->getMessage(), 'file' => $e->getFile(), @@ -191,86 +228,6 @@ final class session extends core ]; } - return false; - } - - /** - * Write - * - * Write a property into an instance of the ArangoDB document - * - * @param string $name Name of the property - * @param mixed $value Content of the property - * - * @return void - */ - public function __set(string $name, mixed $value = null): void - { - // Write to the property into an instance of the ArangoDB document and exit (success) - $this->document->{$name} = $value; - } - - /** - * Read - * - * Read a property from an instance of the ArangoDB docuemnt - * - * @param string $name Name of the property - * - * @return mixed Content of the property - */ - public function __get(string $name): mixed - { - // Read a property from an instance of the ArangoDB document and exit (success) - return match ($name) { - 'arangodb' => $this::$arangodb, - default => $this->document->{$name} - }; - } - - /** - * Delete - * - * Deinitialize the property in an instance of the ArangoDB document - * - * @param string $name Name of the property - * - * @return void - */ - public function __unset(string $name): void - { - // Delete the property in an instance of the ArangoDB document and exit (success) - unset($this->document->{$name}); - } - - /** - * Check of initialization - * - * Check of initialization of the property into an instance of the ArangoDB document - * - * @param string $name Name of the property - * - * @return bool The property is initialized? - */ - public function __isset(string $name): bool - { - // Check of initializatio nof the property and exit (success) - return isset($this->document->{$name}); - } - - /** - * Execute a method - * - * Execute a method from an instance of the ArangoDB document - * - * @param string $name Name of the method - * @param array $arguments Arguments for the method - * - * @return mixed Result of execution of the method - */ - public function __call(string $name, array $arguments = []): mixed - { - // Execute the method and exit (success) - if (method_exists($this->document, $name)) return $this->document->{$name}($arguments); - } -} + // Exit (fail) + return null; + }} diff --git a/author/project/system/models/traits/buffer.php b/author/project/system/models/traits/buffer.php new file mode 100755 index 0000000..4308bb8 --- /dev/null +++ b/author/project/system/models/traits/buffer.php @@ -0,0 +1,84 @@ + + */ +trait buffer +{ + /** + * Write to buffer of the document + * + * @param array $data Data for writing (merge) + * @param array &$errors Registry of errors + * + * @return bool Is data has written into the document from ArangoDB? + */ + public function write(array $data, array &$errors = []): bool + { + try { + if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) { + // Initialized the collection + + // Is the instance of the document from ArangoDB are initialized? + if (!isset($this->document)) throw new exception('The instance of the sessoin document from ArangoDB is not initialized'); + + // Writing data into buffer of the instance of the document from ArangoDB + $this->document->buffer = array_replace_recursive($this->document->buffer ?? [], $data); + + // Is the buffer of the instance of the document from ArangoDB exceed 10 megabytes? + if (mb_strlen(json_encode($this->document->buffer)) > 10485760) throw new exception('The buffer size exceeds 10 megabytes'); + + // Serializing parameters + if ($this->document->language instanceof language) $this->document->language = $this->document->language->name; + + // Writing to ArangoDB and exit (success) + return document::update($this->document, errors: $errors); + } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); + } catch (exception $e) { + // Writing to the registry of errors + $errors[] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + + // Exit (fail) + return false; + } +} diff --git a/author/project/system/models/traits/document.php b/author/project/system/models/traits/document.php new file mode 100755 index 0000000..8ba42a7 --- /dev/null +++ b/author/project/system/models/traits/document.php @@ -0,0 +1,206 @@ + + */ +trait document +{ + /** + * Document + * + * @var _document $document An instance of the document from ArangoDB + */ + protected readonly _document $document; + + /** + * Constructor + * + * @param bool $initialize Initialize a model? + * @param ?arangodb $arangodb Instance of a session of ArangoDB + * @param _document|null|false $document An instance of the ArangoDB document + * + * @return void + */ + public function __construct( + bool $initialize = true, + ?arangodb $arangodb = null, + _document|null|false $document = false + ) { + // For the extends system + parent::__construct($initialize, $arangodb); + + // Writing to the property + if ($document instanceof _document) $this->__document($document); + else if ($document === null) throw new exception('Failed to initialize an instance of the document from ArangoDB'); + } + + /** + * Write or read document + * + * @param _document|null $document Instance of document from ArangoDB + * + * @return _document|null Instance of document from ArangoDB + */ + public function __document(?_document $document = null): ?_document + { + // Writing a property storing a document instance to ArangoDB + if ($document) $this->document ??= $document; + + // Read a property storing a document instance to ArangoDB and exit (success) + return $this->document ?? null; + } + + /** + * Connect + * + * @param collecton_interface $document Document + * @param array &$errors Registry of errors + * + * @return string|null The identifier of the created edge of the "connect" collection, if created + */ + public function connect(collection_interface $document, array &$errors = []): ?string + { + try { + if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) { + if (collection::initialize(connect::COLLECTION, connect::TYPE, errors: $errors)) { + if (collection::initialize($document::COLLECTION, $document::TYPE, errors: $errors)) { + // Initialized collections + + if ($this->document instanceof _document) { + // Initialized instance of the document from ArangoDB + + // Writing document and exit (success) + return framework_document::write( + connect::COLLECTION, + [ + '_from' => $document->getId(), + '_to' => $this->document->getId() + ], + errors: $errors + ); + } else throw new exception('The instance of the document from ArangoDB is not initialized'); + } else throw new exception('Failed to initialize ' . $document::TYPE . ' collection: ' . $document::COLLECTION); + } else throw new exception('Failed to initialize ' . connect::TYPE . ' collection: ' . connect::COLLECTION); + } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); + } catch (exception $e) { + // Writing to the registry of errors + $errors[] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + + // Exit (fail) + return null; + } + + /** + * Write + * + * Write a property into an instance of the ArangoDB document + * + * @param string $name Name of the property + * @param mixed $value Content of the property + * + * @return void + */ + public function __set(string $name, mixed $value = null): void + { + // Writing to the property into an instance of the ArangoDB document and exit (success) + $this->document->{$name} = $value; + } + + /** + * Read + * + * Read a property from an instance of the ArangoDB docuemnt + * + * @param string $name Name of the property + * + * @return mixed Content of the property + */ + public function __get(string $name): mixed + { + // Read a property from an instance of the ArangoDB document and exit (success) + return match ($name) { + default => $this->document->{$name} + }; + } + + /** + * Delete + * + * Deinitialize the property in an instance of the ArangoDB document + * + * @param string $name Name of the property + * + * @return void + */ + public function __unset(string $name): void + { + // Delete the property in an instance of the ArangoDB document and exit (success) + unset($this->document->{$name}); + } + + /** + * Check of initialization + * + * Check of initialization of the property into an instance of the ArangoDB document + * + * @param string $name Name of the property + * + * @return bool The property is initialized? + */ + public function __isset(string $name): bool + { + // Check of initializatio nof the property and exit (success) + return isset($this->document->{$name}); + } + + /** + * Execute a method + * + * Execute a method from an instance of the ArangoDB document + * + * @param string $name Name of the method + * @param array $arguments Arguments for the method + * + * @return mixed Result of execution of the method + */ + public function __call(string $name, array $arguments = []): mixed + { + // Execute the method and exit (success) + return method_exists($this->document, $name) ? $this->document->{$name}($arguments) ?? null : null; + } +} diff --git a/author/project/system/models/traits/files.php b/author/project/system/models/traits/files.php new file mode 100755 index 0000000..ada69bb --- /dev/null +++ b/author/project/system/models/traits/files.php @@ -0,0 +1,66 @@ + + */ +trait files +{ + /** + * Delete + * + * Delete files recursively + * + * @param string $directory Directory + * @param array &$errors Registry of errors + * + * @return void + */ + private static function delete(string $directory, array &$errors = []): void + { + try { + if (file_exists($directory)) { + // Directory exists + + // Deleting descendant files and directories (enter to the recursion) + foreach (scandir($directory) as $file) { + if ($file === '.' || $file === '..') continue; + else if (is_dir("$directory/$file")) static::delete("$directory/$file", $errors); + else unlink("$directory/$file"); + } + + // Deleting the directory + rmdir($directory); + + // Exit (success) + return; + } else throw new exception('Directory does not exist'); + } catch (exception $e) { + // Writing to the registry of errors + $errors[] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + + // Exit (fail) + return; + } +} diff --git a/author/project/system/models/traits/status.php b/author/project/system/models/traits/status.php old mode 100644 new mode 100755 index b2798c2..68fe733 --- a/author/project/system/models/traits/status.php +++ b/author/project/system/models/traits/status.php @@ -4,24 +4,38 @@ declare(strict_types=1); namespace ${REPO_OWNER}\${REPO_NAME}\models\traits; +// Files of the project +use ${REPO_OWNER}\${REPO_NAME}\models\traits\document as document_trait, + ${REPO_OWNER}\${REPO_NAME}\models\interfaces\document as document_interface; + // Built-in libraries use exception; /** - * Trait fo initialization of a status + * Status (DUMB SHIT) + * + * Trait for initialization of a status + * + * @uses document_trait + * @uses document_interface + * + * @method bool|null status(array &$errors) Check document by its status * * @package ${REPO_OWNER}\${REPO_NAME}\models\traits * - * @author ${REPO_OWNER} < mail > + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author ${REPO_OWNER} */ trait status { /** - * Initialize of a status + * Status + * + * Check document by its status * * @param array &$errors Registry of errors * - * @return ?bool Status, if they are found + * @return ?bool Status, if found */ public function status(array &$errors = []): ?bool { @@ -29,7 +43,7 @@ trait status // Read from ArangoDB and exit (success) return $this->document->active ?? false; } catch (exception $e) { - // Write to the registry of errors + // Writing to the registry of errors $errors[] = [ 'text' => $e->getMessage(), 'file' => $e->getFile(), diff --git a/author/project/system/public/css/themes/default/main.css b/author/project/system/public/css/themes/default/main.css old mode 100644 new mode 100755 index ff80335..25ff309 --- a/author/project/system/public/css/themes/default/main.css +++ b/author/project/system/public/css/themes/default/main.css @@ -18,7 +18,6 @@ body { background-color: var(--background, #fafafa); } - aside { } diff --git a/author/project/system/public/index.php b/author/project/system/public/index.php old mode 100644 new mode 100755 index b7f0238..9618cbb --- a/author/project/system/public/index.php +++ b/author/project/system/public/index.php @@ -4,29 +4,40 @@ declare(strict_types=1); namespace ${REPO_OWNER}\${REPO_NAME}; -use mirzaev\minimal\core; -use mirzaev\minimal\router; +// Framework for PHP +use mirzaev\minimal\core, + mirzaev\minimal\route; +// Enabling debugging /* ini_set('error_reporting', E_ALL); ini_set('display_errors', 1); ini_set('display_startup_errors', 1); */ +// Initializing path to the public directory +define('PUBLIC', __DIR__); + +// Initializing path to the project root directory +define('ROOT', INDEX . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR); + +// Initializing path to the directory of views define('VIEWS', realpath('..' . DIRECTORY_SEPARATOR . 'views')); + +// Initializing path to the directory of the storage define('STORAGE', realpath('..' . DIRECTORY_SEPARATOR . 'storage')); -define('INDEX', __DIR__); -// Автозагрузка -require __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php'; +// Initializing default theme for the views templater +define('THEME', 'default'); -// Инициализация маршрутизатора -$router = new router; +// Initializing dependencies +require ROOT . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php'; -// Запись маршрутов -$router->write('/', 'index', 'index'); +// Initializing core +$core = new core(namespace: __NAMESPACE__); -// Инициализация ядра -$core = new core(namespace: __NAMESPACE__, router: $router); - -// Обработка запроса -echo $core->start(); +// Initializing routes +$router->router + ->write('/', new route('index', 'index'), 'GET') +; +// Handling request +$core->start(); diff --git a/author/project/system/settings/.gitignore b/author/project/system/settings/.gitignore old mode 100644 new mode 100755 diff --git a/author/project/system/settings/arangodb.php.sample b/author/project/system/settings/arangodb.php.sample old mode 100644 new mode 100755 diff --git a/author/project/system/views/templater.php b/author/project/system/views/templater.php old mode 100644 new mode 100755 index b85176a..6c3db15 --- a/author/project/system/views/templater.php +++ b/author/project/system/views/templater.php @@ -6,7 +6,7 @@ namespace ${REPO_OWNER}\${REPO_NAME}\views; // Files of the project use ${REPO_OWNER}\${REPO_NAME}\models\session, - ${REPO_OWNER}\${REPO_NAME}\models\account; + ${REPO_OWNER}\${REPO_NAME}\models\enumerations\language; // Framework for PHP use mirzaev\minimal\controller; @@ -15,61 +15,72 @@ use mirzaev\minimal\controller; use Twig\Loader\FilesystemLoader, Twig\Environment as twig, Twig\Extra\Intl\IntlExtension as intl, - Twig\TwigFilter; - + Twig\TwigFilter, + Twig\TwigFunction; // Built-in libraries -use ArrayAccess; +use ArrayAccess as array_access, + Error as error; /** - * Templater core + * Templater * * @package ${REPO_OWNER}\${REPO_NAME}\views - * @author ${REPO_OWNER} < mail > + * + * @param twig $twig Instance of the twig templater + * @param array $variables Registry of view global variables + * + * @method void __construct(?session &$session) Constructor + * @method string|null render(string $file, ?array $variables) Render the HTML-document + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author ${REPO_OWNER} */ -final class templater extends controller implements ArrayAccess +final class templater extends controller implements array_access { /** - * Registry of global variables of view - */ - public array $variables = []; - - /** - * Instance of twig templater + * Twig + * + * @var twig $twig Instance of the twig templater */ readonly public twig $twig; + /** + * Variables + * + * @var array $variables Registry of view global variables + */ + public array $variables = []; + /** * Constructor of an instance * - * @param ?session $session Instance of the session of ArangoDB + * @param ?session $session Instance of the session in ArangoDB * * @return void */ - public function __construct(?session &$session = null): void + public function __construct(?session &$session = null) { - // Initializing of an instance of twig + // Initializing the Twig instance $this->twig = new twig(new FilesystemLoader(VIEWS)); - // Initializing of global variables + // Initializing global variables $this->twig->addGlobal('theme', 'default'); $this->twig->addGlobal('server', $_SERVER); $this->twig->addGlobal('cookies', $_COOKIE); - if (!empty($session->status())) { - $this->twig->addGlobal('session', $session); - } - - // Initializing of twig extensions - $this->twig->addExtension(new intl()); + if (!empty($session->status())) $this->twig->addGlobal('session', $session); + $this->twig->addGlobal('language', $language = $session?->buffer['language'] ?? language::en); } /** - * Render a HTML-document + * Render + * + * Render the HTML-document * * @param string $file Related path to a HTML-document * @param ?array $variables Registry of variables to push into registry of global variables * - * @return ?string HTML-документ + * @return ?string HTML-document */ public function render(string $file, ?array $variables = null): ?string { @@ -80,7 +91,7 @@ final class templater extends controller implements ArrayAccess /** * Write * - * Write a variable into registry of global variables + * Write the variable into the registry of the view global variables * * @param string $name Name of the variable * @param mixed $value Value of the variable @@ -96,7 +107,7 @@ final class templater extends controller implements ArrayAccess /** * Read * - * Read a variable from registry of global variables + * Read the variable from the registry of the view global variables * * @param string $name Name of the variable * @@ -111,7 +122,7 @@ final class templater extends controller implements ArrayAccess /** * Delete * - * Delete a variable from the registry of global variables + * Delete the variable from the registry of the view global variables * * @param string $name Name of the variable * @@ -126,7 +137,7 @@ final class templater extends controller implements ArrayAccess /** * Check of initialization * - * Check of initialization in registry of global variables + * Check of initialization in the registry of the view global variables * * @param string $name Name of the variable * @@ -141,7 +152,7 @@ final class templater extends controller implements ArrayAccess /** * Write * - * Write a variable into registry of global variables + * Write the variable into the registry of the view global variables * * @param mixed $name Name of an offset of the variable * @param mixed $value Value of the variable @@ -157,7 +168,7 @@ final class templater extends controller implements ArrayAccess /** * Read * - * Read a variable from registry of global variables + * Read the variable from the registry of the view global variables * * @param mixed $name Name of the variable * @@ -172,7 +183,7 @@ final class templater extends controller implements ArrayAccess /** * Delete * - * Delete a variable from the registry of global variables + * Delete the variable from the registry of the view global variables * * @param mixed $name Name of the variable * @@ -187,7 +198,7 @@ final class templater extends controller implements ArrayAccess /** * Check of initialization * - * Check of initialization in registry of global variables + * Check of initialization in the registry of the view global variables * * @param mixed $name Name of the variable * diff --git a/author/project/system/views/themes/default/core.html b/author/project/system/views/themes/default/core.html old mode 100644 new mode 100755 diff --git a/author/project/system/views/themes/default/footer.html b/author/project/system/views/themes/default/footer.html old mode 100644 new mode 100755 diff --git a/author/project/system/views/themes/default/head.html b/author/project/system/views/themes/default/head.html old mode 100644 new mode 100755 diff --git a/author/project/system/views/themes/default/header.html b/author/project/system/views/themes/default/header.html old mode 100644 new mode 100755 diff --git a/author/project/system/views/themes/default/index.html b/author/project/system/views/themes/default/index.html old mode 100644 new mode 100755 diff --git a/author/project/system/views/themes/default/js.html b/author/project/system/views/themes/default/js.html old mode 100644 new mode 100755 diff --git a/composer.json b/composer.json index ddfc3e1..b655e36 100755 --- a/composer.json +++ b/composer.json @@ -3,7 +3,9 @@ "description": "${REPO_DESCRIPTION}", "homepage": "https://git.mirzaev.sexy${REPO_LINK}", "type": "site", - "keywords": [], + "keywords": [ + "minimal" + ], "readme": "README.md", "license": "WTFPL", "authors": [ @@ -15,17 +17,17 @@ } ], "support": { - "docs": "https://git.mirzaev.sexy${REPO_LINK}/manual", + "wiki": "https://git.mirzaev.sexy${REPO_LINK}/wiki", "issues": "https://git.mirzaev.sexy${REPO_LINK}/issues" }, "require": { - "php": "~8.3", - "ext-sodium": "~8.3", - "mirzaev/minimal": "^2.2.0", - "mirzaev/accounts": "~1.2.x-dev", - "mirzaev/arangodb": "^1.0.0", - "triagens/arangodb": "~3.9.x-dev", - "twig/twig": "^3.4" + "php": "^8.4", + "triagens/arangodb": "^3.8", + "mirzaev/minimal": "^3.2.0", + "mirzaev/arangodb": "^1.3", + "twig/twig": "^3.10", + "twig/extra-bundle": "^3.7", + "twig/intl-extra": "^3.10" }, "require-dev": { "phpunit/phpunit": "~9.5"