forked from mirzaev/minimal
		
	executing requests from a controller + boosted by PHP 8.4 + the router rebuild. hell fucking yeah
This commit is contained in:
		@@ -1,7 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "mirzaev/minimal",
 | 
			
		||||
    "type": "framework",
 | 
			
		||||
    "description": "Lightweight MVC framework that manages only the basic mechanisms, leaving the development of the programmer and not overloading the project",
 | 
			
		||||
    "description": "My vision of a good framework",
 | 
			
		||||
    "keywords": [
 | 
			
		||||
        "mvc",
 | 
			
		||||
        "framework",
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
            "name": "Arsen Mirzaev Tatyano-Muradovich",
 | 
			
		||||
            "email": "arsen@mirzaev.sexy",
 | 
			
		||||
            "homepage": "https://mirzaev.sexy",
 | 
			
		||||
            "role": "Developer"
 | 
			
		||||
            "role": "Programmer"
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "support": {
 | 
			
		||||
@@ -22,16 +22,11 @@
 | 
			
		||||
        "issues": "https://git.mirzaev.sexy/mirzaev/minimal/issues"
 | 
			
		||||
    },
 | 
			
		||||
    "require": {
 | 
			
		||||
        "php": "~8.2"
 | 
			
		||||
        "php": "~8.4"
 | 
			
		||||
    },
 | 
			
		||||
    "autoload": {
 | 
			
		||||
        "psr-4": {
 | 
			
		||||
            "mirzaev\\minimal\\": "mirzaev/minimal/system"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    "autoload-dev": {
 | 
			
		||||
        "psr-4": {
 | 
			
		||||
            "mirzaev\\minimal\\tests\\": "mirzaev/minimal/tests"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,13 +6,22 @@ namespace mirzaev\minimal;
 | 
			
		||||
 | 
			
		||||
// Files of the project
 | 
			
		||||
use mirzaev\minimal\model,
 | 
			
		||||
	mirzaev\minimal\core,
 | 
			
		||||
	mirzaev\minimal\traits\magic;
 | 
			
		||||
 | 
			
		||||
// Встроенные библиотеки
 | 
			
		||||
// Build-in libraries
 | 
			
		||||
use exception;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Controller (base)
 | 
			
		||||
 * Controller
 | 
			
		||||
 *
 | 
			
		||||
 * @var core $core An instance of the core
 | 
			
		||||
 * @var model $model An instance of the model connected in the core
 | 
			
		||||
 * @var view $view View template engine instance (twig)
 | 
			
		||||
 * @var core $core An instance of the core
 | 
			
		||||
 * @var core $core An instance of the core
 | 
			
		||||
 *
 | 
			
		||||
 * @method self __construct(core $core) Constructor
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\minimal
 | 
			
		||||
 *
 | 
			
		||||
@@ -24,71 +33,54 @@ class controller
 | 
			
		||||
	use magic;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Postfix of file names
 | 
			
		||||
	 * Core
 | 
			
		||||
	 *
 | 
			
		||||
	 * @var core $core An instance of the core
 | 
			
		||||
	 */
 | 
			
		||||
	public const string POSTFIX = '_controller';
 | 
			
		||||
	public core $core {		
 | 
			
		||||
		// Read
 | 
			
		||||
		get => $this->core;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Instance of the model connected in the router
 | 
			
		||||
	 * Model
 | 
			
		||||
	 *
 | 
			
		||||
	 * @var model $model An instance of the model connected in the core
 | 
			
		||||
	 */
 | 
			
		||||
	protected model $model;
 | 
			
		||||
	public model $model {
 | 
			
		||||
		// Write
 | 
			
		||||
		set (model $model) {
 | 
			
		||||
				$this->model ??= $model;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Read
 | 
			
		||||
		get => $this->model;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * View template engine instance (twig)
 | 
			
		||||
	 * View
 | 
			
		||||
	 *
 | 
			
		||||
	 * @var view $view View template engine instance (twig)
 | 
			
		||||
	 */
 | 
			
		||||
	protected object $view;
 | 
			
		||||
	public object $view {
 | 
			
		||||
		// Write
 | 
			
		||||
		set (object $view) {
 | 
			
		||||
				$this->view ??= $view;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Read
 | 
			
		||||
		get => $this->view;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Constructor
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param core $core The instance of the core
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return self
 | 
			
		||||
	 */
 | 
			
		||||
	public function __construct() {}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Write property
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param string $name Name of the property
 | 
			
		||||
	 * @param mixed $value Value of the property
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return void
 | 
			
		||||
	 */
 | 
			
		||||
	public function __set(string $name, mixed $value = null): void
 | 
			
		||||
	{
 | 
			
		||||
		match ($name) {
 | 
			
		||||
			'model' => (function () use ($value) {
 | 
			
		||||
				if ($this->__isset('model')) throw new exception('Can not reinitialize property: ' . static::class . '::$model', 500);
 | 
			
		||||
				else {
 | 
			
		||||
					// Property not initialized 
 | 
			
		||||
 | 
			
		||||
					if (is_object($value)) $this->model = $value;
 | 
			
		||||
					else throw new exception('Property "' . static::class . '::view" should store an instance of a model', 500);
 | 
			
		||||
				}
 | 
			
		||||
			})(),
 | 
			
		||||
			'view' => (function () use ($value) {
 | 
			
		||||
				if ($this->__isset('view')) throw new exception('Can not reinitialize property: ' . static::class . '::$view', 500);
 | 
			
		||||
				else {
 | 
			
		||||
					// Property not initialized 
 | 
			
		||||
 | 
			
		||||
					if (is_object($value)) $this->view = $value;
 | 
			
		||||
					else throw new exception('Property "' . static::class . '::view" should store an instance of a view template engine', 500);
 | 
			
		||||
				}
 | 
			
		||||
			})(),
 | 
			
		||||
			default => throw new exception('Property "' . static::class . "::\$$name\" not found", 404)
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Read property
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param string $name Name of the property
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return mixed Value of the property
 | 
			
		||||
	 */
 | 
			
		||||
	public function __get(string $name): mixed
 | 
			
		||||
	{
 | 
			
		||||
		return match ($name) {
 | 
			
		||||
			'model' => $this->model ?? throw new exception('Property "' . static::class . '::$model" is not initialized', 500),
 | 
			
		||||
			'view' => $this->view ?? throw new exception('Property "' . static::class . '::$view" is not initialized', 500),
 | 
			
		||||
			default => throw new exception('Property "' . static::class . "::\$$name\" not found", 404)
 | 
			
		||||
		};
 | 
			
		||||
	public function __construct(core $core) {
 | 
			
		||||
		// Writing the core into the property
 | 
			
		||||
		$this->core = $core;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,18 +4,33 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\minimal;
 | 
			
		||||
 | 
			
		||||
// Файлы проекта
 | 
			
		||||
// Files of the project
 | 
			
		||||
use mirzaev\minimal\router,
 | 
			
		||||
	mirzaev\minimal\route,
 | 
			
		||||
	mirzaev\minimal\controller,
 | 
			
		||||
	mirzaev\minimal\model;
 | 
			
		||||
 | 
			
		||||
// Встроенные библиотеки
 | 
			
		||||
// Built-in libraries
 | 
			
		||||
use exception,
 | 
			
		||||
	BadMethodCallException  as exception_method,
 | 
			
		||||
	DomainException as exception_domain,
 | 
			
		||||
	InvalidArgumentException as exception_argument,
 | 
			
		||||
	UnexpectedValueException as exception_value,
 | 
			
		||||
	ReflectionClass as reflection;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Core
 | 
			
		||||
 *
 | 
			
		||||
 * @param string $namespace Namespace where the core was initialized from
 | 
			
		||||
 * @param controller $controller An instance of the controller
 | 
			
		||||
 * @param model $model An instance of the model
 | 
			
		||||
 * @param router $router An instance of the router
 | 
			
		||||
 *
 | 
			
		||||
 * @mathod self __construct(?string $namespace) Constructor
 | 
			
		||||
 * @method void __destruct() Destructor
 | 
			
		||||
 * @method string|null request(?string $uri, ?string $method, array $variabls) Handle the request
 | 
			
		||||
 * @method string|null route(route $route, string $method) Handle the route
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\minimal
 | 
			
		||||
 *
 | 
			
		||||
 * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
 | 
			
		||||
@@ -24,204 +39,326 @@ use exception,
 | 
			
		||||
final class core
 | 
			
		||||
{
 | 
			
		||||
	/**
 | 
			
		||||
	 * Инстанция соединения с базой данных
 | 
			
		||||
	 */
 | 
			
		||||
	private object $db;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Инстанция маршрутизатора
 | 
			
		||||
	 */
 | 
			
		||||
	private readonly router $router;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Инстанция ядра контроллера
 | 
			
		||||
	 */
 | 
			
		||||
	private readonly controller $controller;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Инстанция ядра модели
 | 
			
		||||
	 */
 | 
			
		||||
	private readonly model $model;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Путь пространства имён (системное)
 | 
			
		||||
	 * Namespace
 | 
			
		||||
	 *
 | 
			
		||||
	 * Используется для поиска файлов по спецификации PSR-4
 | 
			
		||||
	 */
 | 
			
		||||
	private readonly string $namespace;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Конструктор
 | 
			
		||||
	 * @var string $namespace Namespace where the core was initialized from
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param ?object $db Инстанция соединения с базой данных
 | 
			
		||||
	 * @param ?router $router Маршрутизатор
 | 
			
		||||
	 * @param ?controller $controller Инстанция ядра контроллера
 | 
			
		||||
	 * @param ?model $model Инстанция ядра модели
 | 
			
		||||
	 * @param ?string $namespace Пространство имён системного ядра
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return self Инстанция ядра
 | 
			
		||||
	 * @see https://www.php-fig.org/psr/psr-4/
 | 
			
		||||
	 */
 | 
			
		||||
	public function __construct(
 | 
			
		||||
		?object $db = null,
 | 
			
		||||
		?router $router = null,
 | 
			
		||||
		?controller $controller = null,
 | 
			
		||||
		?model $model = null,
 | 
			
		||||
		?string $namespace = null
 | 
			
		||||
	) {
 | 
			
		||||
		// Инициализация свойств
 | 
			
		||||
		if (isset($db)) $this->__set('db', $db);
 | 
			
		||||
		if (isset($router)) $this->__set('router', $router);
 | 
			
		||||
		if (isset($controller)) $this->__set('controller', $controller);
 | 
			
		||||
		if (isset($model)) $this->__set('model', $model);
 | 
			
		||||
		$this->__set('namespace', $namespace ?? (new reflection(self::class))->getNamespaceName());
 | 
			
		||||
	public string $namespace {
 | 
			
		||||
		// Read
 | 
			
		||||
		get => $this->namespace;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Деструктор
 | 
			
		||||
	 * Controller
 | 
			
		||||
	 *
 | 
			
		||||
	 * @var controller $controller An instance of the controller
 | 
			
		||||
	 */
 | 
			
		||||
	private controller $controller {
 | 
			
		||||
		// Read
 | 
			
		||||
		get => $this->controller ??= new controller;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Model
 | 
			
		||||
	 *
 | 
			
		||||
	 * @var model $model An instance of the model
 | 
			
		||||
	 */
 | 
			
		||||
	private model $model {
 | 
			
		||||
		// Read
 | 
			
		||||
		get => $this->model ??= new model;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Router
 | 
			
		||||
	 *
 | 
			
		||||
	 * @var router $router An instance of the router
 | 
			
		||||
	 */
 | 
			
		||||
	public router $router {
 | 
			
		||||
		get => $this->router ??= router::initialize();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Constrictor
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param ?string $namespace Пространство имён системного ядра
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @return self The instance of the core
 | 
			
		||||
	 */
 | 
			
		||||
	public function __construct(
 | 
			
		||||
		?string $namespace = null
 | 
			
		||||
	) {
 | 
			
		||||
		// Writing a namespace to the property
 | 
			
		||||
		$this->namespace = $namespace ?? (new reflection(self::class))->getNamespaceName();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Destructor
 | 
			
		||||
	 */
 | 
			
		||||
	public function __destruct() {}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Запуск
 | 
			
		||||
	 * Request
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param ?string $uri Маршрут
 | 
			
		||||
	 * Handle the request
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return string|int|null Ответ
 | 
			
		||||
	 * @param string|null $uri URI of the request (value by default: $_SERVER['REQUEST_URI'])
 | 
			
		||||
	 * @param string|null $method Method of the request (GET, POST, PUT...) (value by default: $_SERVER["REQUEST_METHOD"])
 | 
			
		||||
	 * @paam array $parameters parameters for merging with route parameters 
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return string|null Response 
 | 
			
		||||
	 */
 | 
			
		||||
	public function start(string $uri = null): string|int|null
 | 
			
		||||
	public function request(?string $uri = null, ?string $method = null, array $parameters = []): ?string
 | 
			
		||||
	{
 | 
			
		||||
		// Обработка запроса
 | 
			
		||||
		return $this->__get('router')->handle($uri, core: $this);
 | 
			
		||||
		// Matching a route 
 | 
			
		||||
		$route = $this->router->match($uri ??= $_SERVER['REQUEST_URI'] ?? '/', $method ??= $_SERVER["REQUEST_METHOD"]);
 | 
			
		||||
 | 
			
		||||
		if ($route) {
 | 
			
		||||
			// Initialized the route
 | 
			
		||||
 | 
			
		||||
			if (!empty($parameters)) {
 | 
			
		||||
				// Recaived parameters
 | 
			
		||||
 | 
			
		||||
				// Merging parameters with route parameters
 | 
			
		||||
				$route->parameters = $parameters + $route->parameters;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Handling the route and exit (success)
 | 
			
		||||
			return $this->route($route, $method);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Exit (fail)
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Записать свойство
 | 
			
		||||
	 * Route
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param string $name Название
 | 
			
		||||
	 * @param mixed $value Содержимое
 | 
			
		||||
	 * Handle the route
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return void
 | 
			
		||||
	 * @param route $route The route
 | 
			
		||||
	 * @param string $method Method of requests (GET, POST, PUT, DELETE, COOKIE...)
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return string|null Response, if generated
 | 
			
		||||
	 */
 | 
			
		||||
	public function __set(string $name, mixed $value = null): void
 | 
			
		||||
	public function route(route $route, string $method = 'GET'): ?string
 | 
			
		||||
	{
 | 
			
		||||
		match ($name) {
 | 
			
		||||
			'db', 'database' => (function () use ($value) {
 | 
			
		||||
				if ($this->__isset('db')) throw new exception('Запрещено реинициализировать инстанцию соединения с базой данных ($this->db)', 500);
 | 
			
		||||
				else {
 | 
			
		||||
					// Свойство ещё не было инициализировано
 | 
			
		||||
		// Initializing name of the controller class
 | 
			
		||||
		$controller = $route->controller;
 | 
			
		||||
	
 | 
			
		||||
					if (is_object($value)) $this->db = $value;
 | 
			
		||||
					else throw new exception('Свойство $this->db должно хранить инстанцию соединения с базой данных', 500);
 | 
			
		||||
				}
 | 
			
		||||
			})(),
 | 
			
		||||
			'router' => (function () use ($value) {
 | 
			
		||||
				if ($this->__isset('router')) throw new exception('Запрещено реинициализировать инстанцию маршрутизатора ($this->router)', 500);
 | 
			
		||||
				else {
 | 
			
		||||
					// Свойство ещё не было инициализировано
 | 
			
		||||
		if ($route->controller instanceof controller) {
 | 
			
		||||
			// Initialized the controller
 | 
			
		||||
		} else if (class_exists($controller = "$this->namespace\\controllers\\$controller")) {
 | 
			
		||||
			// Found the controller by its name
 | 
			
		||||
 | 
			
		||||
					if ($value instanceof router) $this->router = $value;
 | 
			
		||||
					else throw new exception('Свойство $this->router должно хранить инстанцию маршрутизатора (mirzaev\minimal\router)"', 500);
 | 
			
		||||
				}
 | 
			
		||||
			})(),
 | 
			
		||||
			'controller' => (function () use ($value) {
 | 
			
		||||
				if ($this->__isset('controller')) throw new exception('Запрещено реинициализировать инстанцию ядра контроллеров ($this->controller)', 500);
 | 
			
		||||
				else {
 | 
			
		||||
					// Свойство не инициализировано
 | 
			
		||||
			// Initializing the controller
 | 
			
		||||
			$route->controller = new $controller(core: $this);
 | 
			
		||||
		} else if (!empty($route->controller)) {
 | 
			
		||||
			// Not found the controller and $route->controller has a value
 | 
			
		||||
 | 
			
		||||
					if ($value instanceof controller) $this->controller = $value;
 | 
			
		||||
					else throw new exception('Свойство $this->controller должно хранить инстанцию ядра контроллеров (mirzaev\minimal\controller)', 500);
 | 
			
		||||
				}
 | 
			
		||||
			})(),
 | 
			
		||||
			'model' => (function () use ($value) {
 | 
			
		||||
				if ($this->__isset('model')) throw new exception('Запрещено реинициализировать инстанцию ядра моделей ($this->model)', 500);
 | 
			
		||||
				else {
 | 
			
		||||
					// Свойство не инициализировано
 | 
			
		||||
			// Exit (fail)
 | 
			
		||||
			throw new exception_domain('Failed to found the controller: ' . $route->controller);
 | 
			
		||||
		} else {
 | 
			
		||||
			// Not found the controller and $route->controller is empty
 | 
			
		||||
 | 
			
		||||
					if ($value instanceof model) $this->model = $value;
 | 
			
		||||
					else throw new exception('Свойство $this->model должно хранить инстанцию ядра моделей (mirzaev\minimal\model)', 500);
 | 
			
		||||
				}
 | 
			
		||||
			})(),
 | 
			
		||||
			'namespace' => (function () use ($value) {
 | 
			
		||||
				if ($this->__isset('namespace')) throw new exception('Запрещено реинициализировать путь пространства имён ($this->namespace)', 500);
 | 
			
		||||
				else {
 | 
			
		||||
					// Свойство не инициализировано
 | 
			
		||||
 | 
			
		||||
					if (is_string($value)) $this->namespace = $value;
 | 
			
		||||
					else throw new exception('Свойство $this->namespace должно хранить строку с путём пространства имён', 500);
 | 
			
		||||
				}
 | 
			
		||||
			})(),
 | 
			
		||||
			default => throw new exception("Свойство \"\$$name\" не найдено", 404)
 | 
			
		||||
		};
 | 
			
		||||
			// Exit (fail)
 | 
			
		||||
			throw new exception_argument('Failed to initialize the controller: ' . $route->controller);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Прочитать свойство
 | 
			
		||||
	 *
 | 
			
		||||
	 * Записывает значение по умолчанию, если свойство не инициализировано
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param string $name Название
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return mixed Содержимое
 | 
			
		||||
	 */
 | 
			
		||||
	public function __get(string $name): mixed
 | 
			
		||||
	{
 | 
			
		||||
		return match ($name) {
 | 
			
		||||
			'db', 'database' => $this->db ?? throw new exception("Свойство \"\$$name\" не инициализировано", 500),
 | 
			
		||||
			'router' => (function () {
 | 
			
		||||
				// Инициализация со значением по умолчанию
 | 
			
		||||
				if (!$this->__isset('router')) $this->__set('router', new router);
 | 
			
		||||
		// Deinitializing name of the controller class
 | 
			
		||||
		unset($controller);
 | 
			
		||||
 | 
			
		||||
				// Возврат (успех)
 | 
			
		||||
				return $this->router;
 | 
			
		||||
			})(),
 | 
			
		||||
			'controller' => (function () {
 | 
			
		||||
				// Инициализация со значением по умолчанию
 | 
			
		||||
				if (!$this->__isset('controller')) $this->__set('controller', new controller);
 | 
			
		||||
		// Initializing name if the model class
 | 
			
		||||
		$model = $route->model;
 | 
			
		||||
 | 
			
		||||
				// Возврат (успех)
 | 
			
		||||
				return $this->controller;
 | 
			
		||||
			})(),
 | 
			
		||||
			'model' => (function () {
 | 
			
		||||
				// Инициализация со значением по умолчанию
 | 
			
		||||
				if (!$this->__isset('model')) $this->__set('model', new model);
 | 
			
		||||
		if ($route->model instanceof model) {
 | 
			
		||||
			// Initialized the model
 | 
			
		||||
		} else if (class_exists($model = "$this->namespace\\models\\$model")) {
 | 
			
		||||
			// Found the model by its name
 | 
			
		||||
 | 
			
		||||
				// Возврат (успех)
 | 
			
		||||
				return $this->model;
 | 
			
		||||
			})(),
 | 
			
		||||
			'namespace' => $this->namespace ?? throw new exception("Свойство \"\$$name\" не инициализировано", 500),
 | 
			
		||||
			default => throw new exception("Свойство \"\$$name\" не найдено", 404)
 | 
			
		||||
		};
 | 
			
		||||
			// Initializing the model
 | 
			
		||||
			$route->model = new $model;
 | 
			
		||||
		} else if (!empty($route->model)) {
 | 
			
		||||
			// Not found the model and $route->model has a value
 | 
			
		||||
 | 
			
		||||
			// Exit (fail)
 | 
			
		||||
			throw new exception_domain('Failed to initialize the model: ' . ($route->model ?? $route->controller));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Проверить свойство на инициализированность
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param string $name Название
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return bool Инициализировано свойство?
 | 
			
		||||
	 */
 | 
			
		||||
	public function __isset(string $name): bool
 | 
			
		||||
	{
 | 
			
		||||
		return match ($name) {
 | 
			
		||||
			default => isset($this->{$name})
 | 
			
		||||
		};
 | 
			
		||||
		// Deinitializing name of the model class
 | 
			
		||||
		unset($model);
 | 
			
		||||
 | 
			
		||||
		if ($route->model instanceof model) {
 | 
			
		||||
			// Initialized the model
 | 
			
		||||
 | 
			
		||||
			// Writing the model to the controller
 | 
			
		||||
			$route->controller->model = $route->model;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Удалить свойство
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param string $name Название
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return void
 | 
			
		||||
	 */
 | 
			
		||||
	public function __unset(string $name): void
 | 
			
		||||
	{
 | 
			
		||||
		match ($name) {
 | 
			
		||||
			default => (function () use ($name) {
 | 
			
		||||
				// Удаление
 | 
			
		||||
				unset($this->{$name});
 | 
			
		||||
			})()
 | 
			
		||||
		};
 | 
			
		||||
		if ($method === 'POST') {
 | 
			
		||||
			// POST
 | 
			
		||||
 | 
			
		||||
			if (method_exists($route->controller, $route->method)) {
 | 
			
		||||
				// Found the method of the controller
 | 
			
		||||
 | 
			
		||||
				// Executing method of the controller that handles the route and exit (success)
 | 
			
		||||
				return $route->controller->{$route->method}($route->parameters + $_POST, $_FILES);
 | 
			
		||||
			} else {
 | 
			
		||||
				// Not found the method of the controller
 | 
			
		||||
		
 | 
			
		||||
				// Exit (fail)
 | 
			
		||||
				throw new exception('Failed to find the method of the controller: ' . $route->method);
 | 
			
		||||
			}
 | 
			
		||||
		} else if ($method === 'GET') {
 | 
			
		||||
			// GET
 | 
			
		||||
 | 
			
		||||
			if (method_exists($route->controller, $route->method)) {
 | 
			
		||||
				// Found the method of the controller
 | 
			
		||||
 | 
			
		||||
				if ($_SERVER["CONTENT_TYPE"] === 'multipart/form-data' || $_SERVER["CONTENT_TYPE"] === 'application/x-www-form-urlencoded') {
 | 
			
		||||
					// The requeset content type is the "multipart/form-data" or "application/x-www-form-urlencoded"
 | 
			
		||||
 | 
			
		||||
					// Analysis of the request
 | 
			
		||||
					[$_GET, $_FILES] = request_parse_body($route->options);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Executing method of the controller that handles the route and exit (success)
 | 
			
		||||
				return $route->controller->{$route->method}($route->parameters + $_GET, $_FILES);
 | 
			
		||||
			} else {
 | 
			
		||||
				// Not found the method of the controller
 | 
			
		||||
		
 | 
			
		||||
				// Exit (fail)
 | 
			
		||||
				throw new exception('Failed to find the method of the controller: ' . $route->method);
 | 
			
		||||
			}
 | 
			
		||||
		} else if ($method === 'PUT') {
 | 
			
		||||
			// PUT
 | 
			
		||||
 | 
			
		||||
			if (method_exists($route->controller, $route->method)) {
 | 
			
		||||
				// Found the method of the controller
 | 
			
		||||
 | 
			
		||||
				if ($_SERVER["CONTENT_TYPE"] === 'multipart/form-data' || $_SERVER["CONTENT_TYPE"] === 'application/x-www-form-urlencoded') {
 | 
			
		||||
					// The requeset content type is the "multipart/form-data" or "application/x-www-form-urlencoded"
 | 
			
		||||
 | 
			
		||||
					// Analysis of the request
 | 
			
		||||
					[$_PUT, $_FILES] = request_parse_body($route->options);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Executing method of the controller that handles the route and exit (success)
 | 
			
		||||
				return $route->controller->{$route->method}($route->parameters + $_PUT, $_FILES);
 | 
			
		||||
			} else {
 | 
			
		||||
				// Not found the method of the controller
 | 
			
		||||
		
 | 
			
		||||
				// Exit (fail)
 | 
			
		||||
				throw new exception('Failed to find the method of the controller: ' . $route->method);
 | 
			
		||||
			}
 | 
			
		||||
		} else if ($method === 'DELETE') {
 | 
			
		||||
			// DELETE
 | 
			
		||||
 | 
			
		||||
			if (method_exists($route->controller, $route->method)) {
 | 
			
		||||
				// Found the method of the controller
 | 
			
		||||
 | 
			
		||||
				if ($_SERVER["CONTENT_TYPE"] === 'multipart/form-data' || $_SERVER["CONTENT_TYPE"] === 'application/x-www-form-urlencoded') {
 | 
			
		||||
					// The requeset content type is the "multipart/form-data" or "application/x-www-form-urlencoded"
 | 
			
		||||
 | 
			
		||||
					// Analysis of the request
 | 
			
		||||
					[$_DELETE, $_FILES] = request_parse_body($route->options);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Executing method of the controller that handles the route and exit (success)
 | 
			
		||||
				return $route->controller->{$route->method}($route->parameters + $_DELETE, $_FILES);
 | 
			
		||||
			} else {
 | 
			
		||||
				// Not found the method of the controller
 | 
			
		||||
		
 | 
			
		||||
				// Exit (fail)
 | 
			
		||||
				throw new exception('Failed to find the method of the controller: ' . $route->method);
 | 
			
		||||
			}
 | 
			
		||||
		} else if ($method === 'PATCH') {
 | 
			
		||||
			// PATCH
 | 
			
		||||
 | 
			
		||||
			if (method_exists($route->controller, $route->method)) {
 | 
			
		||||
				// Found the method of the controller
 | 
			
		||||
 | 
			
		||||
				if ($_SERVER["CONTENT_TYPE"] === 'multipart/form-data' || $_SERVER["CONTENT_TYPE"] === 'application/x-www-form-urlencoded') {
 | 
			
		||||
					// The requeset content type is the "multipart/form-data" or "application/x-www-form-urlencoded"
 | 
			
		||||
 | 
			
		||||
					// Analysis of the request
 | 
			
		||||
					[$_PATCH, $_FILES] = request_parse_body($route->options);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Executing method of the controller that handles the route and exit (success)
 | 
			
		||||
				return $route->controller->{$route->method}($route->parameters + $_PATCH, $_FILES);
 | 
			
		||||
			} else {
 | 
			
		||||
				// Not found the method of the controller
 | 
			
		||||
		
 | 
			
		||||
				// Exit (fail)
 | 
			
		||||
				throw new exception('Failed to find the method of the controller: ' . $route->method);
 | 
			
		||||
			}
 | 
			
		||||
		} else if ($method === 'HEAD') {
 | 
			
		||||
			// HEAD
 | 
			
		||||
 | 
			
		||||
			if (method_exists($route->controller, $route->method)) {
 | 
			
		||||
				// Found the method of the controller
 | 
			
		||||
 | 
			
		||||
				// Executing method of the controller that handles the route and exit (success)
 | 
			
		||||
				return $route->controller->{$route->method}($route->parameters);
 | 
			
		||||
			} else {
 | 
			
		||||
				// Not found the method of the controller
 | 
			
		||||
		
 | 
			
		||||
				// Exit (fail)
 | 
			
		||||
				throw new exception('Failed to find the method of the controller: ' . $route->method);
 | 
			
		||||
			}
 | 
			
		||||
		} else if ($method === 'OPTIONS') {
 | 
			
		||||
			// OPTIONS
 | 
			
		||||
 | 
			
		||||
			if (method_exists($route->controller, $route->method)) {
 | 
			
		||||
				// Found the method of the controller
 | 
			
		||||
 | 
			
		||||
				// Executing method of the controller that handles the route and exit (success)
 | 
			
		||||
				return $route->controller->{$route->method}($route->parameters);
 | 
			
		||||
			} else {
 | 
			
		||||
				// Not found the method of the controller
 | 
			
		||||
		
 | 
			
		||||
				// Exit (fail)
 | 
			
		||||
				throw new exception('Failed to find the method of the controller: ' . $route->method);
 | 
			
		||||
			}
 | 
			
		||||
		} else if ($method === 'CONNECT') {
 | 
			
		||||
			// CONNECT
 | 
			
		||||
 | 
			
		||||
			if (method_exists($route->controller, $route->method)) {
 | 
			
		||||
				// Found the method of the controller
 | 
			
		||||
 | 
			
		||||
				// Executing method of the controller that handles the route and exit (success)
 | 
			
		||||
				return $route->controller->{$route->method}($route->parameters);
 | 
			
		||||
			} else {
 | 
			
		||||
				// Not found the method of the controller
 | 
			
		||||
		
 | 
			
		||||
				// Exit (fail)
 | 
			
		||||
				throw new exception('Failed to find the method of the controller: ' . $route->method);
 | 
			
		||||
			}
 | 
			
		||||
		} else if ($method === 'TRACE') {
 | 
			
		||||
			// TRACE
 | 
			
		||||
 | 
			
		||||
			if (method_exists($route->controller, $route->method)) {
 | 
			
		||||
				// Found the method of the controller
 | 
			
		||||
 | 
			
		||||
				// Executing method of the controller that handles the route and exit (success)
 | 
			
		||||
				return $route->controller->{$route->method}($route->parameters);
 | 
			
		||||
			} else {
 | 
			
		||||
				// Not found the method of the controller
 | 
			
		||||
		
 | 
			
		||||
				// Exit (fail)
 | 
			
		||||
				throw new exception('Failed to find the method of the controller: ' . $route->method);
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// Not recognized method of the request
 | 
			
		||||
 | 
			
		||||
			// Exit (fail)
 | 
			
		||||
			throw new exception_value('Failed to recognize the method: ' . $method);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Exit (fail)
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,11 +7,10 @@ namespace mirzaev\minimal;
 | 
			
		||||
// Files of the project
 | 
			
		||||
use	mirzaev\minimal\traits\magic;
 | 
			
		||||
 | 
			
		||||
// Built-in libraries
 | 
			
		||||
use exception;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Model (base)
 | 
			
		||||
 * Model
 | 
			
		||||
 *
 | 
			
		||||
 * @method self __construct() Constructor
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\minimal
 | 
			
		||||
 *
 | 
			
		||||
@@ -22,13 +21,10 @@ class model
 | 
			
		||||
{
 | 
			
		||||
	use magic;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Postfix of file names
 | 
			
		||||
	 */
 | 
			
		||||
	public const string POSTFIX = '_model';
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Constructor
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return self
 | 
			
		||||
	 */
 | 
			
		||||
	public function __construct() {}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										127
									
								
								mirzaev/minimal/system/route.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										127
									
								
								mirzaev/minimal/system/route.php
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,127 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\minimal;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Route
 | 
			
		||||
 *
 | 
			
		||||
 * @param string|controller $controller Name of the controller
 | 
			
		||||
 * @param string $method Name of the method of the method of $this->controller
 | 
			
		||||
 * @param string|model $model Name of the model
 | 
			
		||||
 * @param array $parameters Arguments for the $this->method (will be concatenated together with generated request parameters)
 | 
			
		||||
 * @param array $options Options for `request_parse_body($options)`
 | 
			
		||||
 *
 | 
			
		||||
 * @method self __construct(string $controller,?string $method, ?string $model, array $variables, array $options) Constructor
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\minimal
 | 
			
		||||
 *
 | 
			
		||||
 * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
final class route
 | 
			
		||||
{
 | 
			
		||||
	/**
 | 
			
		||||
	 * Controller
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @var string|model $controller Name of the controller or an instance of the controller
 | 
			
		||||
	 */
 | 
			
		||||
	public string|controller $controller {
 | 
			
		||||
		// Read
 | 
			
		||||
		get => $this->controller;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Method
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @var string $method Name of the method of the method of $this->controller
 | 
			
		||||
	 */
 | 
			
		||||
	public string $method{
 | 
			
		||||
		// Read
 | 
			
		||||
		get => $this->method;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Model
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @var string|model|null $model Name of the model of an instance of the model
 | 
			
		||||
	 */
 | 
			
		||||
	public string|model|null $model {
 | 
			
		||||
		// Read
 | 
			
		||||
		get => $this->model;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Parameters
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @var array $parameters Arguments for the $this->method (will be concatenated together with generated request parameters)
 | 
			
		||||
	 */
 | 
			
		||||
	public array $parameters {
 | 
			
		||||
		// Read
 | 
			
		||||
		get => $this->parameters;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Options
 | 
			
		||||
	 *
 | 
			
		||||
	 * Used only for GET, PUT, PATCH and DELETE
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @var string $options Options for `request_parse_body($options)`
 | 
			
		||||
	 */
 | 
			
		||||
	public array $options {
 | 
			
		||||
		// Write
 | 
			
		||||
		set (array $value) => array_filter(
 | 
			
		||||
			$value,
 | 
			
		||||
			fn(string $key) => match ($key) {
 | 
			
		||||
				'post_max_size',
 | 
			
		||||
				'max_input_vars',
 | 
			
		||||
				'max_multipart_body_parts',
 | 
			
		||||
				'max_file_uploads',
 | 
			
		||||
				'upload_max_filesize' => true,
 | 
			
		||||
				default => false
 | 
			
		||||
			},
 | 
			
		||||
			ARRAY_FILTER_USE_KEY
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		// Read
 | 
			
		||||
		get => $this->options;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Constructor
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param string|controller $controller Name of the controller
 | 
			
		||||
	 * @param string|null $method Name of the method of the method of $controller
 | 
			
		||||
	 * @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 $options Options for `request_parse_body` (Only for POST method)
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return self
 | 
			
		||||
	 */
 | 
			
		||||
	public function __construct(
 | 
			
		||||
		string|controller $controller,
 | 
			
		||||
		?string $method = 'index',
 | 
			
		||||
		string|model|null $model = null,
 | 
			
		||||
		array $parameters = [],
 | 
			
		||||
		array $options = []
 | 
			
		||||
	) {
 | 
			
		||||
		// Writing name of the controller
 | 
			
		||||
		$this->controller = $controller;
 | 
			
		||||
 | 
			
		||||
		// Writing name of the model
 | 
			
		||||
		$this->model = $model;
 | 
			
		||||
 | 
			
		||||
		// Writing name of the method of the controller
 | 
			
		||||
		$this->method = $method;
 | 
			
		||||
 | 
			
		||||
		// Writing parameters
 | 
			
		||||
		$this->parameters = $parameters;
 | 
			
		||||
 | 
			
		||||
		// Writing options
 | 
			
		||||
		if (match ($method) {
 | 
			
		||||
			'GET', 'PUT', 'PATCH', 'DELETE' => true,
 | 
			
		||||
			default => false
 | 
			
		||||
		}) $this->options = $options;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -4,11 +4,21 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\minimal;
 | 
			
		||||
 | 
			
		||||
// Файлы проекта
 | 
			
		||||
use mirzaev\minimal\core;
 | 
			
		||||
// Files of the project
 | 
			
		||||
use mirzaev\minimal\route,
 | 
			
		||||
	mirzaev\minimal\traits\singleton;
 | 
			
		||||
 | 
			
		||||
// Build-ing libraries
 | 
			
		||||
use InvalidArgumentException as exception_argument;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Маршрутизатор
 | 
			
		||||
 * Router
 | 
			
		||||
 *
 | 
			
		||||
 * @param array $routes The registry of routes
 | 
			
		||||
 *
 | 
			
		||||
 * @method self write(string $uri, string $method)
 | 
			
		||||
 * @method self sort() Sort routes (DEV)
 | 
			
		||||
 * @method string universalize(string $urn) Universalize URN
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\minimal
 | 
			
		||||
 *
 | 
			
		||||
@@ -17,58 +27,57 @@ use mirzaev\minimal\core;
 | 
			
		||||
 */
 | 
			
		||||
final class router
 | 
			
		||||
{
 | 
			
		||||
	/**
 | 
			
		||||
	 * @var array $router Реестр маршрутов
 | 
			
		||||
	 */
 | 
			
		||||
	/* protected array $routes = []; */
 | 
			
		||||
	public array $routes = [];
 | 
			
		||||
	use singleton;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Записать маршрут
 | 
			
		||||
	 * Routes
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param string $route Маршрут
 | 
			
		||||
	 * @param string $handler Обработчик - инстанции контроллера и модели (не обязательно), без постфиксов
 | 
			
		||||
	 * @param ?string $method Вызываемый метод в инстанции контроллера обработчика
 | 
			
		||||
	 * @param ?string $request HTTP-метод запроса (GET, POST, PUT...)
 | 
			
		||||
	 * @param ?string $model Инстанция модели (переопределение инстанции модели в $target)
 | 
			
		||||
	 * @param array $variables 
 | 
			
		||||
	 * @var array $routes The registry of routes
 | 
			
		||||
	 */
 | 
			
		||||
	protected array $routes = [];
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Write a route
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return static The instance from which the method was called (fluent interface)
 | 
			
		||||
	 * @param string $urn URN of the route ('/', '/page', '/page/$variable', '/page/$collector...'...)
 | 
			
		||||
	 * @param route $route The route
 | 
			
		||||
	 * @param string|array $method Method of requests (GET, POST, PUT, DELETE, COOKIE...)
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return self The instance from which the method was called (fluent interface)
 | 
			
		||||
	 */
 | 
			
		||||
	public function write(
 | 
			
		||||
		string $route,
 | 
			
		||||
		string $handler,
 | 
			
		||||
		?string $method = 'index',
 | 
			
		||||
		?string $request = 'GET',
 | 
			
		||||
		?string $model = null,
 | 
			
		||||
		array $variables = []
 | 
			
		||||
	): static {
 | 
			
		||||
		// Запись в реестр
 | 
			
		||||
		$this->routes[$route][$request] = [
 | 
			
		||||
			'controller' => $handler,
 | 
			
		||||
			'model' => $model ?? $handler,
 | 
			
		||||
			'method' => $method,
 | 
			
		||||
			'variables' => $variables
 | 
			
		||||
		];
 | 
			
		||||
		string $urn,
 | 
			
		||||
		route $route,
 | 
			
		||||
		null|string|array $method = 'GET'
 | 
			
		||||
	): self {
 | 
			
		||||
		foreach (is_array($method) ? $method : [$method] as $method) {
 | 
			
		||||
			// Iterate over methods of requests
 | 
			
		||||
 | 
			
		||||
			// Validating method
 | 
			
		||||
			$method = match ($method) {
 | 
			
		||||
				'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS', 'CONNECT', 'TRACE' => $method,
 | 
			
		||||
				default => throw new exception_argument("Failed to initialize method: \"$method\"")
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			// Writing to the registry of routes
 | 
			
		||||
			$this->routes[$urn][$method] = $route;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Exit (success) (fluent interface)
 | 
			
		||||
		return $this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Handle a request
 | 
			
		||||
	 * Match
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param string|null $uri URI (protocol://domain/foo/bar)
 | 
			
		||||
	 * @param string|null $method Method (GET, POST, PUT...)
 | 
			
		||||
	 * @param core|null $core Instence of the system core
 | 
			
		||||
	 * Match request URI with registry of routes
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return string|int|null Response
 | 
			
		||||
	 * @param string $uri URI (protocol://domain/foo/bar)
 | 
			
		||||
	 * @param string $method Method of the request (GET, POST, PUT...)
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return route|null Route, if found
 | 
			
		||||
	 */
 | 
			
		||||
	public function handle(
 | 
			
		||||
		?string $uri = null,
 | 
			
		||||
		?string $method = null,
 | 
			
		||||
		?core $core = new core
 | 
			
		||||
	): string|int|null {
 | 
			
		||||
	public function match(string $uri, string $method): ?route {
 | 
			
		||||
		// Declaration of the registry of routes directoies 
 | 
			
		||||
		$routes = [];
 | 
			
		||||
 | 
			
		||||
@@ -83,15 +92,8 @@ final class router
 | 
			
		||||
		if (count($routes) === count($this->routes)) {
 | 
			
		||||
			// Initialized the registry of routes directoies
 | 
			
		||||
 | 
			
		||||
			// Initializing default values		
 | 
			
		||||
			$uri ??= $_SERVER['REQUEST_URI'] ?? '/';
 | 
			
		||||
			$method ??= $_SERVER["REQUEST_METHOD"];
 | 
			
		||||
 | 
			
		||||
			// Initializing of URN (/foo/bar)
 | 
			
		||||
			$urn = parse_url(urldecode($uri), PHP_URL_PATH);
 | 
			
		||||
 | 
			
		||||
			// Universalization of URN
 | 
			
		||||
			$urn = self::universalize($urn);
 | 
			
		||||
			// Universalization of URN (/foo/bar)
 | 
			
		||||
			$urn = self::universalize(parse_url(urldecode($uri), PHP_URL_PATH));
 | 
			
		||||
 | 
			
		||||
			// Search directories of URN (explode() creates empty value in array)
 | 
			
		||||
			preg_match_all('/(^\/$|[^\/]+)/', $urn, $directories);
 | 
			
		||||
@@ -171,12 +173,12 @@ final class router
 | 
			
		||||
						// The directory is a variable ($variable)
 | 
			
		||||
 | 
			
		||||
						// Запись в реестр переменных и перещапись директории в маршруте
 | 
			
		||||
						$data[$method]['variables'][trim($route_directory, '$')] = $directories[$i];
 | 
			
		||||
						$data[$method]->variables[trim($route_directory, '$')] = $directories[$i];
 | 
			
		||||
					} else if (preg_match('/^\$([a-zA-Z_\x80-\xff]+\.\.\.)$/', $route_directory) === 1) {
 | 
			
		||||
						// The directory of route is a collector ($variable...)
 | 
			
		||||
 | 
			
		||||
						// Инициализаия ссылки на массив сборщика
 | 
			
		||||
						$collector = &$data[$method]['variables'][trim($route_directory, '$.')];
 | 
			
		||||
						$collector = &$data[$method]->variables[trim($route_directory, '$.')];
 | 
			
		||||
 | 
			
		||||
						// Инициализаия массива сборщика
 | 
			
		||||
						$collector ??= [];
 | 
			
		||||
@@ -195,31 +197,13 @@ final class router
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				/**
 | 
			
		||||
				 * Initialization of route handlers
 | 
			
		||||
				 */
 | 
			
		||||
 | 
			
		||||
				if (array_key_exists($method, $data)) {
 | 
			
		||||
					// Идентифицирован метод маршрута (GET, POST, PUT...)
 | 
			
		||||
 | 
			
		||||
					// Инициализация обработчиков (controller, model, method)
 | 
			
		||||
					$handlers = $data[$method];
 | 
			
		||||
 | 
			
		||||
					if (class_exists($controller = $core->namespace . '\\controllers\\' . $handlers['controller'] . $core->controller::POSTFIX)) {
 | 
			
		||||
						// Найден контроллер
 | 
			
		||||
 | 
			
		||||
						// Инициализация инстанции ядра контроллера
 | 
			
		||||
						$controller = new $controller;
 | 
			
		||||
 | 
			
		||||
						// Вызов связанного с маршрутом метода и возврат (успех)
 | 
			
		||||
						return $controller->{$handlers['method']}($handlers['variables'] + $_REQUEST, $_FILES, file_get_contents('php://input'));
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				// Exit (success or fail)
 | 
			
		||||
				return $data[$method] ?? null;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Возврат (провал)
 | 
			
		||||
		return $this->error($core);
 | 
			
		||||
		// Exit (fail)
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
@@ -236,11 +220,11 @@ final class router
 | 
			
		||||
	 *
 | 
			
		||||
	 * Добавить чтобы сначала текст потом переменная затем после переменной первыми тексты и в конце перменные опять и так рекурсивно
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return static The instance from which the method was called (fluent interface)
 | 
			
		||||
	 * @return self The instance from which the method was called (fluent interface)
 | 
			
		||||
	 *
 | 
			
		||||
	 * @todo ПЕРЕДЕЛАТЬ ПОЛНОСТЬЮ
 | 
			
		||||
	 */
 | 
			
		||||
	public function sort(): static
 | 
			
		||||
	public function sort(): self
 | 
			
		||||
	{
 | 
			
		||||
		uksort($this->routes, function (string $a, string $b) {
 | 
			
		||||
			// Sorting routes
 | 
			
		||||
@@ -284,35 +268,18 @@ final class router
 | 
			
		||||
		return $this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Сгенерировать ответ с ошибкой
 | 
			
		||||
	 *
 | 
			
		||||
	 * Вызывает метод error404 в инстанции контроллера ошибок
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param ?core $core Инстанция системного ядра
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return ?string HTML-документ
 | 
			
		||||
	 */
 | 
			
		||||
	private function error(core $core = new core): ?string
 | 
			
		||||
	{
 | 
			
		||||
		return class_exists($class = '\\' . $core->namespace . '\\controllers\\errors' . $core->controller::POSTFIX)
 | 
			
		||||
			&& method_exists($class, $method = 'error404')
 | 
			
		||||
			? (new $class)->$method()
 | 
			
		||||
			: null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Universalize URN 
 | 
			
		||||
	 *
 | 
			
		||||
	 * Always "/" at the beginning and never "/" at the end
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param string &$urn URN (/foo/bar)
 | 
			
		||||
	 * @param string $urn URN (/foo/bar)
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return string Universalized URN 
 | 
			
		||||
	 */
 | 
			
		||||
	private function universalize(string $urn): string
 | 
			
		||||
	{
 | 
			
		||||
		// Universalization of URN and exit (success)
 | 
			
		||||
		return (string) preg_replace('/(.+)\/$/', '$1', preg_replace('/^([^\/])/', '/$1', $urn));
 | 
			
		||||
		return (string) '/' . mb_trim($urn, '/');
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,12 +10,14 @@ use exception;
 | 
			
		||||
/**
 | 
			
		||||
 * Trait of magical methods
 | 
			
		||||
 *
 | 
			
		||||
 * @method void __set(string $name, mixed $value = null) Write property
 | 
			
		||||
 * @method void __set(string $name, mixed $value) Write property
 | 
			
		||||
 * @method mixed __get(string $name) Read property
 | 
			
		||||
 * @method void __unset(string $name) Delete property
 | 
			
		||||
 * @method bool __isset(string $name) Check property for initialization
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\minimal\traits
 | 
			
		||||
 *
 | 
			
		||||
 * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
trait magic
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										85
									
								
								mirzaev/minimal/system/traits/singleton.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								mirzaev/minimal/system/traits/singleton.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace mirzaev\minimal\traits;
 | 
			
		||||
 | 
			
		||||
// Built-in libraries
 | 
			
		||||
use exception;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Trait of singleton
 | 
			
		||||
 *
 | 
			
		||||
 * @method static initialize() Initialize an instance
 | 
			
		||||
 *
 | 
			
		||||
 * @package mirzaev\minimal\traits
 | 
			
		||||
 *
 | 
			
		||||
 * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
 | 
			
		||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
			
		||||
 */
 | 
			
		||||
trait singleton
 | 
			
		||||
{
 | 
			
		||||
	/**
 | 
			
		||||
	 * Constructor (blocked)
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return void
 | 
			
		||||
	 */
 | 
			
		||||
	final private function __construct()
 | 
			
		||||
	{
 | 
			
		||||
		// Initializing the indicator that an instance of static::class has already been initialized
 | 
			
		||||
		static $instance = false;
 | 
			
		||||
 | 
			
		||||
		if ($instance) {
 | 
			
		||||
			// An instance of static has already been initialized
 | 
			
		||||
 | 
			
		||||
			// Exit (fail)
 | 
			
		||||
			throw new exception('Has already been initialized an instance of the ' . static::class);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Writing the indicator that the instance of static been initialized
 | 
			
		||||
		$instance = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Initialize an instance
 | 
			
		||||
	 *
 | 
			
		||||
	 * Create an instance of static::class, or return an already created static::class instance
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return static
 | 
			
		||||
	 */
 | 
			
		||||
	public static function initialize(): static
 | 
			
		||||
	{
 | 
			
		||||
		// Initialize the buffer of the instance of static::class
 | 
			
		||||
		static $instance;
 | 
			
		||||
 | 
			
		||||
		// Exit (success)
 | 
			
		||||
		return $instance ??= new static;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Clone (blocked)
 | 
			
		||||
	 */
 | 
			
		||||
	private function __clone()
 | 
			
		||||
	{
 | 
			
		||||
		// Exit (fail)
 | 
			
		||||
		throw new exception('Cloning is inadmissible');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Sleep (blocked)
 | 
			
		||||
	 */
 | 
			
		||||
	public function __sleep()
 | 
			
		||||
	{
 | 
			
		||||
		// Exit (fail)
 | 
			
		||||
		throw new exception('Serialization is inadmissible');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Wake up (blocked)
 | 
			
		||||
	 */
 | 
			
		||||
	public function __wakeup()
 | 
			
		||||
	{
 | 
			
		||||
		// Exit (fail)
 | 
			
		||||
		throw new exception('Deserialisation is inadmissible');
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user