1 Commits

Author SHA1 Message Date
e604d19eb1 variables fix with Ksenia 2025-10-25 14:39:20 +03:00
9 changed files with 2424 additions and 241 deletions

1
.gitignore vendored
View File

@@ -1,2 +1 @@
vendor vendor
composer.lock

View File

@@ -14,7 +14,7 @@
"name": "Arsen Mirzaev Tatyano-Muradovich", "name": "Arsen Mirzaev Tatyano-Muradovich",
"email": "arsen@mirzaev.sexy", "email": "arsen@mirzaev.sexy",
"homepage": "https://mirzaev.sexy", "homepage": "https://mirzaev.sexy",
"role": "Creator" "role": "Programmer"
} }
], ],
"support": { "support": {
@@ -22,15 +22,7 @@
"issues": "https://git.svoboda.works/mirzaev/minimal/issues" "issues": "https://git.svoboda.works/mirzaev/minimal/issues"
}, },
"require": { "require": {
"php": "~8.4", "php": "~8.4"
"mobiledetect/mobiledetectlib": "^4.8"
},
"suggest": {
"mirzaev/baza": "Baza database",
"mirzaev/pot": "Template for projects",
"mirzaev/files": "Easy working with files",
"mirzaev/languages": "Easy languages integration",
"mirzaev/currencies": "Easy currencies integration"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

2320
composer.lock generated Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -6,26 +6,23 @@ namespace mirzaev\minimal;
// Files of the project // Files of the project
use mirzaev\minimal\router, use mirzaev\minimal\router,
mirzaev\minimal\route, mirzaev\minimal\route,
mirzaev\minimal\controller, mirzaev\minimal\controller,
mirzaev\minimal\model, mirzaev\minimal\model,
mirzaev\minimal\http\request, mirzaev\minimal\http\request,
mirzaev\minimal\http\response, mirzaev\minimal\http\response,
mirzaev\minimal\http\enumerations\status; mirzaev\minimal\http\enumerations\status;
// Built-in libraries // Built-in libraries
use Closure as closure, use Closure as closure,
Exception as exception, Exception as exception,
RuntimeException as exception_runtime, RuntimeException as exception_runtime,
BadMethodCallException as exception_method, BadMethodCallException as exception_method,
DomainException as exception_domain, DomainException as exception_domain,
InvalidArgumentException as exception_argument, InvalidArgumentException as exception_argument,
UnexpectedValueException as exception_value, UnexpectedValueException as exception_value,
LogicException as exception_logic, LogicException as exception_logic,
Error as error, ReflectionClass as reflection;
ArgumentCountError as error_argument_count,
ReflectionClass as reflection,
ReflectionMethod as reflection_method;
/** /**
* Core * Core
@@ -37,7 +34,7 @@ use Closure as closure,
* @param model $model An instance of the model * @param model $model An instance of the model
* @param router $router An instance of the router * @param router $router An instance of the router
* *
* @method void __construct(?string $namespace) Constructor * @mathod void __construct(?string $namespace) Constructor
* @method void __destruct() Destructor * @method void __destruct() Destructor
* @method string|null start() Initialize request by environment and handle it * @method string|null start() Initialize request by environment and handle it
* @method string|null request(request $request, array $parameters = []) Handle request * @method string|null request(request $request, array $parameters = []) Handle request
@@ -106,7 +103,9 @@ final class core
/** /**
* Destructor * Destructor
*/ */
public function __destruct() {} public function __destruct()
{
}
/** /**
* Start * Start
@@ -258,37 +257,8 @@ final class core
// Writing the request options from the route options // Writing the request options from the route options
$request->options = $route->options; $request->options = $route->options;
// Initializing the controller method arguments // Processing the method of the controller and exit (success)
$arguments = $route->parameters + $route->variables + $request->parameters; $action = fn(): string => (string) $route->controller->{$route->method}(...($route->parameters + $route->variables + $request->parameters));
// Processing the controller method and exit (success)
$action = function () use ($route, $request, $arguments): string {
if (!isset($route->options['controller_method_arguments'])) {
// Not initialized the option
// Skipping
goto nothing;
}
if ($route->options['controller_method_arguments'] === 'strict') {
// Strict arguments (spread operator)
// Exit (success)
return (string) $route->controller->{$route->method}(...$arguments);
} else if ($route->options['controller_method_arguments'] === 'array') {
// The array argument (all in one)
// Exit (success)
return (string) $route->controller->{$route->method}($arguments ? $arguments : null);
} else {
// Nothing
nothing:
// Exit (success)
return (string) $route->controller->{$route->method}();
}
};
foreach ($route->middlewares as $middleware) { foreach ($route->middlewares as $middleware) {
// Iterating over the route middlewares // Iterating over the route middlewares

View File

@@ -11,9 +11,6 @@ use mirzaev\minimal\http\enumerations\method,
mirzaev\minimal\http\enumerations\content, mirzaev\minimal\http\enumerations\content,
mirzaev\minimal\http\response; mirzaev\minimal\http\response;
// The smartphones detection library
use Detection\MobileDetect as mobile;
// Built-in libraries // Built-in libraries
use DomainException as exception_domain, use DomainException as exception_domain,
InvalidArgumentException as exception_argument, InvalidArgumentException as exception_argument,
@@ -290,7 +287,7 @@ final class request
'max_multipart_body_parts', 'max_multipart_body_parts',
'max_file_uploads', 'max_file_uploads',
'upload_max_filesize' => true, 'upload_max_filesize' => true,
default => false default => throw new exception_domain("Failed to recognize option: $key", status::internal_server_error->value)
}, },
ARRAY_FILTER_USE_KEY ARRAY_FILTER_USE_KEY
); );
@@ -300,50 +297,6 @@ final class request
get => $this->options ?? []; get => $this->options ?? [];
} }
/**
* Smartphone
*
* @see https://docs.mobiledetect.net/home/usage-composer Documentation
*
* @var bool $smartphone The request was sent from a smartphone?
*/
public bool $smartphone {
// Read
get {
if (!isset($this->smartphone)) {
// The property is not initialized
// Writing into the property
$this->smartphone = new mobile()->isMobile();
}
// Exit (success)
return $this->smartphone;
}
}
/**
* Tablet
*
* @see https://docs.mobiledetect.net/home/usage-composer Documentation
*
* @var bool $tablet The request was sent from a tablet?
*/
public bool $tablet {
// Read
get {
if (!isset($this->tablet)) {
// The property is not initialized
// Writing into the property
$this->tablet = new mobile()->isTablet();
}
// Exit (success)
return $this->tablet;
}
}
/** /**
* Constructor * Constructor
* *
@@ -455,6 +408,9 @@ final class request
// Exit (false) // Exit (false)
throw new exception_argument('Failed to validate JSON from the input buffer', status::unprocessable_content->value); throw new exception_argument('Failed to validate JSON from the input buffer', status::unprocessable_content->value);
} }
// Writing parameters from environment into the property
$this->parameters = $_POST ?? [];
} else if ($this->method === method::post) { } else if ($this->method === method::post) {
// POST method // POST method

View File

@@ -10,9 +10,7 @@ use mirzaev\minimal\http\request,
mirzaev\minimal\route; mirzaev\minimal\route;
// Built-in libraries // Built-in libraries
use Closure as closure, use Closure as closure;
LogicException as exception_logic
;
/** /**
* Middleware * Middleware
@@ -28,14 +26,14 @@ use Closure as closure,
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/ */
class middleware final class middleware
{ {
/** /**
* Function * Function
* *
* @var closure|array $function Function * @var closure $function Function
*/ */
public readonly closure|array $function; public readonly closure $function;
/** /**
* Constructor * Constructor
@@ -44,28 +42,10 @@ class middleware
* *
* @return void * @return void
*/ */
public function __construct(?closure $function = null) public function __construct(closure $function)
{ {
if (static::class === self::class) {
// The middleware class itself
// Writing the function // Writing the function
$this->function = $function; $this->function = $function;
} else {
// The middleware inheriting class
if (method_exists($this, 'middleware')) {
// Found the method
// Writing the function
$this->function = [$this, 'middleware'];
} else {
// Not found the method
// Exit (fail)
throw new exception_logic('The middleware method is not initialized', 500);
}
}
} }
/** /**

View File

@@ -7,13 +7,8 @@ namespace mirzaev\minimal;
// Files of the project // Files of the project
use mirzaev\minimal\controller, use mirzaev\minimal\controller,
mirzaev\minimal\middleware, mirzaev\minimal\middleware,
mirzaev\minimal\http\enumerations\status,
mirzaev\minimal\traits\middleware as middleware_trait; mirzaev\minimal\traits\middleware as middleware_trait;
// Built-in libraries
use RuntimeException as exception_runtime,
DomainException as exception_domain;
/** /**
* Route * Route
* *
@@ -69,8 +64,6 @@ final class route
/** /**
* Parameters * Parameters
* *
* Defined by request
*
* @see https://wiki.php.net/rfc/property-hooks (find a table about backed and virtual hooks) * @see https://wiki.php.net/rfc/property-hooks (find a table about backed and virtual hooks)
* *
* @var array $parameters Arguments for the $this->method (will be concatenated together with generated request parameters) * @var array $parameters Arguments for the $this->method (will be concatenated together with generated request parameters)
@@ -80,30 +73,17 @@ final class route
&get => $this->parameters; &get => $this->parameters;
} }
/**
* Variables
*
* Defined by route URN
*
* @see https://wiki.php.net/rfc/property-hooks (find a table about backed and virtual hooks)
*
* @var array $variables Arguments for the $this->method (will be concatenated together with generated request parameters)
*/
public array $variables = [] {
// Read
&get => $this->variables;
}
/** /**
* Options * Options
* *
* Required if $this->method !== method::post
*
* @see https://wiki.php.net/rfc/rfc1867-non-post About request_parse_body() * @see https://wiki.php.net/rfc/rfc1867-non-post About request_parse_body()
* @see https://wiki.php.net/rfc/property-hooks Hooks (find a table about backed and virtual hooks) * @see https://wiki.php.net/rfc/property-hooks Hooks (find a table about backed and virtual hooks)
* *
* @throws exception_runtime when reinitializing the property * @throws exception_runtime if reinitialize the property
* @throws exception_domain if not recognied the option
* *
* @var array $options Options * @var array $options Options for `request_parse_body($options)`
*/ */
public array $options { public array $options {
// Write // Write
@@ -123,9 +103,8 @@ final class route
'max_input_vars', 'max_input_vars',
'max_multipart_body_parts', 'max_multipart_body_parts',
'max_file_uploads', 'max_file_uploads',
'upload_max_filesize', 'upload_max_filesize' => true,
'controller_method_arguments' => true, default => throw new exception_domain("Failed to recognize option: $key", status::internal_server_error->value)
default => throw new exception_domain("Failed to recognize the option: $key", status::internal_server_error->value)
}, },
ARRAY_FILTER_USE_KEY ARRAY_FILTER_USE_KEY
); );
@@ -135,6 +114,18 @@ final class route
get => $this->options ?? []; get => $this->options ?? [];
} }
/**
* Parameters
*
* @see https://wiki.php.net/rfc/property-hooks (find a table about backed and virtual hooks)
*
* @var array $parameters Arguments for the $this->method (will be concatenated together with generated request parameters)
*/
public array $variables = [] {
// Read
&get => $this->variables;
}
/** /**
* Constructor * Constructor
* *
@@ -142,7 +133,7 @@ final class route
* @param string|null $method Name of the method of the method of $controller * @param string|null $method Name of the method of the method of $controller
* @param string|model|null $model Name of the model * @param string|model|null $model Name of the model
* @param array $parameters Arguments for the $method (will be concatenated together with generated request parameters) * @param array $parameters Arguments for the $method (will be concatenated together with generated request parameters)
* @param array $options Options * @param array $options Options for `request_parse_body` (Only for POST method)
* @param array $middlewares Middlewares stack * @param array $middlewares Middlewares stack
* *
* @return void * @return void
@@ -167,9 +158,6 @@ final class route
// Writing parameters // Writing parameters
$this->parameters = $parameters; $this->parameters = $parameters;
// Writing options
$this->options = $options;
// Declaring the register of the middlewares stack validity // Declaring the register of the middlewares stack validity
$stack = true; $stack = true;
@@ -195,5 +183,11 @@ final class route
// Writing the middlewares stack // Writing the middlewares stack
$this->middlewares = $middlewares; $this->middlewares = $middlewares;
} }
// Writing options
if (match ($method) {
'GET', 'PUT', 'PATCH', 'DELETE' => true,
default => false
}) $this->options = $options;
} }
} }

View File

@@ -57,13 +57,13 @@ final class router
*/ */
public function write(string $urn, route $route, string|array $method): self public function write(string $urn, route $route, string|array $method): self
{ {
foreach (is_array($method) ? $method : [$method] as $_method) { foreach (is_array($method) ? $method : [$method] as $method) {
// Iterate over methods of requests // Iterate over methods of requests
// Initializing the request // Initializing the request
$request = new request( $request = new request(
uri: $urn, uri: $urn,
method: $_method, method: $method,
environment: false environment: false
); );
@@ -129,7 +129,6 @@ final class router
// Skipping unmatched routes based on results of previous iterations // Skipping unmatched routes based on results of previous iterations
if (isset($matches[$route]) && $matches[$route] === false) continue; if (isset($matches[$route]) && $matches[$route] === false) continue;
// Initializing of route directory // Initializing of route directory
$route_directory = $routes[$route][$i] ?? null; $route_directory = $routes[$route][$i] ?? null;
@@ -162,30 +161,17 @@ final class router
} }
} }
if (array_all($matches, fn($value) => $value === false)) { // Finding a priority route from match results
// Not found any route
if (false) {
// Initialized the errors controller
// Processing the `404` error method
}
} else {
// Found At least one route
// Finding a priority route from all match results (setting the $route variable)
foreach ($matches as $route => $match) if ($match !== false) break; foreach ($matches as $route => $match) if ($match !== false) break;
unset($match);
if ($route && !empty($data = $this->routes[$route])) { if ($route && !empty($data = $this->routes[$route])) {
// The route found // Route found
// Universalization of the route // Universalization of route
$route = self::universalize($route); $route = self::universalize($route);
/** /**
* Initialization of the route variables * Initialization of route variables
*/ */
foreach ($routes[$route] as $i => $route_directory) { foreach ($routes[$route] as $i => $route_directory) {
@@ -194,7 +180,7 @@ final class router
if (preg_match('/^\$([a-zA-Z_\x80-\xff]+)$/', $route_directory) === 1) { if (preg_match('/^\$([a-zA-Z_\x80-\xff]+)$/', $route_directory) === 1) {
// The directory is a variable ($variable) // The directory is a variable ($variable)
// Запись в реестр переменных и перезапись директории в маршруте // Запись в реестр переменных и перещапись директории в маршруте
$data[$request->method->value]->variables[trim($route_directory, '$')] = $directories[$i]; $data[$request->method->value]->variables[trim($route_directory, '$')] = $directories[$i];
} else if (preg_match('/^\$([a-zA-Z_\x80-\xff]+\.\.\.)$/', $route_directory) === 1) { } else if (preg_match('/^\$([a-zA-Z_\x80-\xff]+\.\.\.)$/', $route_directory) === 1) {
// The directory of route is a collector ($variable...) // The directory of route is a collector ($variable...)
@@ -223,9 +209,8 @@ final class router
return $data[$request->method->value] ?? null; return $data[$request->method->value] ?? null;
} }
} }
}
// Exit (success/fail) // Exit (fail)
return null; return null;
} }

View File

@@ -8,8 +8,7 @@ namespace mirzaev\minimal\traits;
use mirzaev\minimal\http\enumerations\status; use mirzaev\minimal\http\enumerations\status;
// Built-in libraries // Built-in libraries
use Exception as exception, use exception;
DomainException as exception_domain;
/** /**
* Trait of magical methods * Trait of magical methods
@@ -29,8 +28,6 @@ trait magic
/** /**
* Write property * Write property
* *
* @throws exception_domain Not found the proprty
*
* @param string $name Name of the property * @param string $name Name of the property
* @param mixed $value Value of the property * @param mixed $value Value of the property
* *
@@ -38,24 +35,14 @@ trait magic
*/ */
public function __set(string $name, mixed $value = null): void public function __set(string $name, mixed $value = null): void
{ {
if (property_exists(static::static, $name)) { match ($name) {
// Exist the property default => throw new exception('Failed to find property: ' . static::class . "::\$$name", status::not_found->value)
};
// Writing the property
$this->{$name} = $value;
} else {
// Not exist the property
// Exit (fail)
throw new exception_domain('Not found the property: ' . static::class . "::\$$name", status::not_found->value);
}
} }
/** /**
* Read property * Read property
* *
* @throws exception_domain Not found the property
*
* @param string $name Name of the property * @param string $name Name of the property
* *
* @return mixed Value of the property * @return mixed Value of the property
@@ -63,7 +50,7 @@ trait magic
public function __get(string $name): mixed public function __get(string $name): mixed
{ {
return match ($name) { return match ($name) {
default => throw new exception_domain('Not found the property: ' . static::class . "::\$$name", status::not_found->value) default => throw new exception('Failed to find property: ' . static::class . "::\$$name", status::not_found->value)
}; };
} }