forked from mirzaev/minimal
		
	collectors, new router, errors handlers, refactoring
This commit is contained in:
		@@ -4,119 +4,91 @@ declare(strict_types=1);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace mirzaev\minimal;
 | 
					namespace mirzaev\minimal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Файлы проекта
 | 
					// Files of the project
 | 
				
			||||||
use mirzaev\minimal\model;
 | 
					use mirzaev\minimal\model,
 | 
				
			||||||
 | 
						mirzaev\minimal\traits\magic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Встроенные библиотеки
 | 
					// Встроенные библиотеки
 | 
				
			||||||
use exception;
 | 
					use exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Контроллер
 | 
					 * Controller (base)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @package mirzaev\minimal
 | 
					 * @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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class controller
 | 
					class controller
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  /**
 | 
						use magic;
 | 
				
			||||||
   * Постфикс
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  public const POSTFIX = '_controller';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Инстанция модели
 | 
						 * Postfix of file names
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  protected model $model;
 | 
						public const string POSTFIX = '_controller';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Инстанция шаблонизатора представления
 | 
						 * Instance of the model connected in the router
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  protected object $view;
 | 
						protected model $model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Конструктор
 | 
						 * View template engine instance (twig)
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  public function __construct()
 | 
						protected object $view;
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Записать свойство
 | 
						 * Constructor
 | 
				
			||||||
   *
 | 
						 */
 | 
				
			||||||
   * @param string $name Название
 | 
						public function __construct() {}
 | 
				
			||||||
   * @param mixed $value Значение
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @return void
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  public function __set(string $name, mixed $value = null): void
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    match ($name) {
 | 
					 | 
				
			||||||
      'model' => (function () use ($value) {
 | 
					 | 
				
			||||||
        if ($this->__isset('model')) throw new exception('Запрещено реинициализировать свойство с инстанцией модели ($this->model)', 500);
 | 
					 | 
				
			||||||
        else {
 | 
					 | 
				
			||||||
          // Свойство не инициализировано
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (is_object($value)) $this->model = $value;
 | 
						/**
 | 
				
			||||||
          else throw new exception('Свойство $this->model должно хранить инстанцию модели (объект)', 500);
 | 
						 * Write property
 | 
				
			||||||
        }
 | 
						 *
 | 
				
			||||||
      })(),
 | 
						 * @param string $name Name of the property
 | 
				
			||||||
      'view' => (function () use ($value) {
 | 
						 * @param mixed $value Value of the property
 | 
				
			||||||
        if ($this->__isset('view')) throw new exception('Запрещено реинициализировать свойство с инстанцией шаблонизатора представления ($this->view)', 500);
 | 
						 *
 | 
				
			||||||
        else {
 | 
						 * @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->view = $value;
 | 
										if (is_object($value)) $this->model = $value;
 | 
				
			||||||
          else throw new exception('Свойство $this->view должно хранить инстанцию шаблонизатора представления (объект)', 500);
 | 
										else throw new exception('Property "' . static::class . '::view" should store an instance of a model', 500);
 | 
				
			||||||
        }
 | 
									}
 | 
				
			||||||
      })(),
 | 
								})(),
 | 
				
			||||||
      default => throw new exception("Свойство \"\$$name\" не найдено", 404)
 | 
								'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);
 | 
				
			||||||
   *
 | 
									}
 | 
				
			||||||
   * @param string $name Название
 | 
								})(),
 | 
				
			||||||
   *
 | 
								default => throw new exception('Property "' . static::class . "::\$$name\" not found", 404)
 | 
				
			||||||
   * @return mixed Содержимое
 | 
							};
 | 
				
			||||||
   */
 | 
						}
 | 
				
			||||||
  public function __get(string $name): mixed
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    return match ($name) {
 | 
					 | 
				
			||||||
      'model' => $this->model ?? throw new exception("Свойство \"\$model\" не инициализировано", 500),
 | 
					 | 
				
			||||||
      'view' => $this->view ?? throw new exception("Свойство \"\$view\" не инициализировано", 500),
 | 
					 | 
				
			||||||
      default => throw new exception("Свойство \"\$$name\" не обнаружено", 404)
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Проверить свойство на инициализированность
 | 
						 * Read property
 | 
				
			||||||
   *
 | 
						 *
 | 
				
			||||||
   * @param string $name Название
 | 
						 * @param string $name Name of the property
 | 
				
			||||||
   * 
 | 
						 *
 | 
				
			||||||
   * @return bool Инициализировано свойство?
 | 
						 * @return mixed Value of the property
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  public function __isset(string $name): bool
 | 
						public function __get(string $name): mixed
 | 
				
			||||||
  {
 | 
						{
 | 
				
			||||||
    return match ($name) {
 | 
							return match ($name) {
 | 
				
			||||||
      default => isset($this->{$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)
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
  /**
 | 
						}
 | 
				
			||||||
   * Удалить свойство
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @param string $name Название
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @return void
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  public function __unset(string $name): void
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    match ($name) {
 | 
					 | 
				
			||||||
      default => (function () use ($name) {
 | 
					 | 
				
			||||||
        // Удаление
 | 
					 | 
				
			||||||
        unset($this->{$name});
 | 
					 | 
				
			||||||
      })()
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,225 +6,222 @@ namespace mirzaev\minimal;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Файлы проекта
 | 
					// Файлы проекта
 | 
				
			||||||
use mirzaev\minimal\router,
 | 
					use mirzaev\minimal\router,
 | 
				
			||||||
  mirzaev\minimal\controller,
 | 
						mirzaev\minimal\controller,
 | 
				
			||||||
  mirzaev\minimal\model;
 | 
						mirzaev\minimal\model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Встроенные библиотеки
 | 
					// Встроенные библиотеки
 | 
				
			||||||
use exception,
 | 
					use exception,
 | 
				
			||||||
  ReflectionClass as reflection;
 | 
						ReflectionClass as reflection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Ядро
 | 
					 * Core
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @package mirzaev\minimal
 | 
					 * @package mirzaev\minimal
 | 
				
			||||||
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
					 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @todo
 | 
					 * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
 | 
				
			||||||
 * 1. Добавить __isset() и __unset()
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class core
 | 
					final class core
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Инстанция соединения с базой данных
 | 
						 * Инстанция соединения с базой данных
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  private object $db;
 | 
						private object $db;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Инстанция маршрутизатора
 | 
						 * Инстанция маршрутизатора
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  private readonly router $router;
 | 
						private readonly router $router;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Инстанция ядра контроллера
 | 
						 * Инстанция ядра контроллера
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  private readonly controller $controller;
 | 
						private readonly controller $controller;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Инстанция ядра модели
 | 
						 * Инстанция ядра модели
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  private readonly model $model;
 | 
						private readonly model $model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Путь пространства имён (системное)
 | 
						 * Путь пространства имён (системное)
 | 
				
			||||||
   *
 | 
						 *
 | 
				
			||||||
   * Используется для поиска файлов по спецификации PSR-4
 | 
						 * Используется для поиска файлов по спецификации PSR-4
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  private readonly string $namespace;
 | 
						private readonly string $namespace;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Конструктор
 | 
						 * Конструктор
 | 
				
			||||||
   *
 | 
						 *
 | 
				
			||||||
   * @param ?object $db Инстанция соединения с базой данных
 | 
						 * @param ?object $db Инстанция соединения с базой данных
 | 
				
			||||||
   * @param ?router $router Маршрутизатор
 | 
						 * @param ?router $router Маршрутизатор
 | 
				
			||||||
   * @param ?controller $controller Инстанция ядра контроллера
 | 
						 * @param ?controller $controller Инстанция ядра контроллера
 | 
				
			||||||
   * @param ?model $model Инстанция ядра модели
 | 
						 * @param ?model $model Инстанция ядра модели
 | 
				
			||||||
   * @param ?string $namespace Пространство имён системного ядра
 | 
						 * @param ?string $namespace Пространство имён системного ядра
 | 
				
			||||||
   * 
 | 
						 * 
 | 
				
			||||||
   * @return self Инстанция ядра
 | 
						 * @return self Инстанция ядра
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  public function __construct(
 | 
						public function __construct(
 | 
				
			||||||
    ?object $db = null,
 | 
							?object $db = null,
 | 
				
			||||||
    ?router $router = null,
 | 
							?router $router = null,
 | 
				
			||||||
    ?controller $controller = null,
 | 
							?controller $controller = null,
 | 
				
			||||||
    ?model $model = null,
 | 
							?model $model = null,
 | 
				
			||||||
    ?string $namespace = null
 | 
							?string $namespace = null
 | 
				
			||||||
  ) {
 | 
						) {
 | 
				
			||||||
    // Инициализация свойств
 | 
							// Инициализация свойств
 | 
				
			||||||
    if (isset($db)) $this->__set('db', $db);
 | 
							if (isset($db)) $this->__set('db', $db);
 | 
				
			||||||
    if (isset($router)) $this->__set('router', $router);
 | 
							if (isset($router)) $this->__set('router', $router);
 | 
				
			||||||
    if (isset($controller)) $this->__set('controller', $controller);
 | 
							if (isset($controller)) $this->__set('controller', $controller);
 | 
				
			||||||
    if (isset($model)) $this->__set('model', $model);
 | 
							if (isset($model)) $this->__set('model', $model);
 | 
				
			||||||
    $this->__set('namespace', $namespace ?? (new reflection(self::class))->getNamespaceName());
 | 
							$this->__set('namespace', $namespace ?? (new reflection(self::class))->getNamespaceName());
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Деструктор
 | 
						 * Деструктор
 | 
				
			||||||
   *
 | 
						 *
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  public function __destruct()
 | 
						public function __destruct() {}
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Запуск
 | 
						 * Запуск
 | 
				
			||||||
   *
 | 
						 *
 | 
				
			||||||
   * @param ?string $uri Маршрут
 | 
						 * @param ?string $uri Маршрут
 | 
				
			||||||
   *
 | 
						 *
 | 
				
			||||||
   * @return string|int|null Ответ
 | 
						 * @return string|int|null Ответ
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  public function start(string $uri = null): string|int|null
 | 
						public function start(string $uri = null): string|int|null
 | 
				
			||||||
  {
 | 
						{
 | 
				
			||||||
    // Обработка запроса
 | 
							// Обработка запроса
 | 
				
			||||||
    return $this->__get('router')->handle($uri, core: $this);
 | 
							return $this->__get('router')->handle($uri, core: $this);
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Записать свойство
 | 
						 * Записать свойство
 | 
				
			||||||
   *
 | 
						 *
 | 
				
			||||||
   * @param string $name Название
 | 
						 * @param string $name Название
 | 
				
			||||||
   * @param mixed $value Содержимое
 | 
						 * @param mixed $value Содержимое
 | 
				
			||||||
   *
 | 
						 *
 | 
				
			||||||
   * @return void
 | 
						 * @return void
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  public function __set(string $name, mixed $value = null): void
 | 
						public function __set(string $name, mixed $value = null): void
 | 
				
			||||||
  {
 | 
						{
 | 
				
			||||||
    match ($name) {
 | 
							match ($name) {
 | 
				
			||||||
      'db', 'database' => (function () use ($value) {
 | 
								'db', 'database' => (function () use ($value) {
 | 
				
			||||||
        if ($this->__isset('db')) throw new exception('Запрещено реинициализировать инстанцию соединения с базой данных ($this->db)', 500);
 | 
									if ($this->__isset('db')) throw new exception('Запрещено реинициализировать инстанцию соединения с базой данных ($this->db)', 500);
 | 
				
			||||||
        else {
 | 
									else {
 | 
				
			||||||
          // Свойство ещё не было инициализировано
 | 
										// Свойство ещё не было инициализировано
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (is_object($value)) $this->db = $value;
 | 
										if (is_object($value)) $this->db = $value;
 | 
				
			||||||
          else throw new exception('Свойство $this->db должно хранить инстанцию соединения с базой данных', 500);
 | 
										else throw new exception('Свойство $this->db должно хранить инстанцию соединения с базой данных', 500);
 | 
				
			||||||
        }
 | 
									}
 | 
				
			||||||
      })(),
 | 
								})(),
 | 
				
			||||||
      'router' => (function () use ($value) {
 | 
								'router' => (function () use ($value) {
 | 
				
			||||||
        if ($this->__isset('router')) throw new exception('Запрещено реинициализировать инстанцию маршрутизатора ($this->router)', 500);
 | 
									if ($this->__isset('router')) throw new exception('Запрещено реинициализировать инстанцию маршрутизатора ($this->router)', 500);
 | 
				
			||||||
        else {
 | 
									else {
 | 
				
			||||||
          // Свойство ещё не было инициализировано
 | 
										// Свойство ещё не было инициализировано
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if ($value instanceof router) $this->router = $value;
 | 
										if ($value instanceof router) $this->router = $value;
 | 
				
			||||||
          else throw new exception('Свойство $this->router должно хранить инстанцию маршрутизатора (mirzaev\minimal\router)"', 500);
 | 
										else throw new exception('Свойство $this->router должно хранить инстанцию маршрутизатора (mirzaev\minimal\router)"', 500);
 | 
				
			||||||
        }
 | 
									}
 | 
				
			||||||
      })(),
 | 
								})(),
 | 
				
			||||||
      'controller' => (function () use ($value) {
 | 
								'controller' => (function () use ($value) {
 | 
				
			||||||
        if ($this->__isset('controller')) throw new exception('Запрещено реинициализировать инстанцию ядра контроллеров ($this->controller)', 500);
 | 
									if ($this->__isset('controller')) throw new exception('Запрещено реинициализировать инстанцию ядра контроллеров ($this->controller)', 500);
 | 
				
			||||||
        else {
 | 
									else {
 | 
				
			||||||
          // Свойство не инициализировано
 | 
										// Свойство не инициализировано
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if ($value instanceof controller) $this->controller = $value;
 | 
										if ($value instanceof controller) $this->controller = $value;
 | 
				
			||||||
          else throw new exception('Свойство $this->controller должно хранить инстанцию ядра контроллеров (mirzaev\minimal\controller)', 500);
 | 
										else throw new exception('Свойство $this->controller должно хранить инстанцию ядра контроллеров (mirzaev\minimal\controller)', 500);
 | 
				
			||||||
        }
 | 
									}
 | 
				
			||||||
      })(),
 | 
								})(),
 | 
				
			||||||
      'model' => (function () use ($value) {
 | 
								'model' => (function () use ($value) {
 | 
				
			||||||
        if ($this->__isset('model')) throw new exception('Запрещено реинициализировать инстанцию ядра моделей ($this->model)', 500);
 | 
									if ($this->__isset('model')) throw new exception('Запрещено реинициализировать инстанцию ядра моделей ($this->model)', 500);
 | 
				
			||||||
        else {
 | 
									else {
 | 
				
			||||||
          // Свойство не инициализировано
 | 
										// Свойство не инициализировано
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if ($value instanceof model) $this->model = $value;
 | 
										if ($value instanceof model) $this->model = $value;
 | 
				
			||||||
          else throw new exception('Свойство $this->model должно хранить инстанцию ядра моделей (mirzaev\minimal\model)', 500);
 | 
										else throw new exception('Свойство $this->model должно хранить инстанцию ядра моделей (mirzaev\minimal\model)', 500);
 | 
				
			||||||
        }
 | 
									}
 | 
				
			||||||
      })(),
 | 
								})(),
 | 
				
			||||||
      'namespace' => (function () use ($value) {
 | 
								'namespace' => (function () use ($value) {
 | 
				
			||||||
        if ($this->__isset('namespace')) throw new exception('Запрещено реинициализировать путь пространства имён ($this->namespace)', 500);
 | 
									if ($this->__isset('namespace')) throw new exception('Запрещено реинициализировать путь пространства имён ($this->namespace)', 500);
 | 
				
			||||||
        else {
 | 
									else {
 | 
				
			||||||
          // Свойство не инициализировано
 | 
										// Свойство не инициализировано
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (is_string($value)) $this->namespace = $value;
 | 
										if (is_string($value)) $this->namespace = $value;
 | 
				
			||||||
          else throw new exception('Свойство $this->namespace должно хранить строку с путём пространства имён', 500);
 | 
										else throw new exception('Свойство $this->namespace должно хранить строку с путём пространства имён', 500);
 | 
				
			||||||
        }
 | 
									}
 | 
				
			||||||
      })(),
 | 
								})(),
 | 
				
			||||||
      default => throw new exception("Свойство \"\$$name\" не найдено", 404)
 | 
								default => throw new exception("Свойство \"\$$name\" не найдено", 404)
 | 
				
			||||||
    };
 | 
							};
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Прочитать свойство
 | 
						 * Прочитать свойство
 | 
				
			||||||
   *
 | 
						 *
 | 
				
			||||||
   * Записывает значение по умолчанию, если свойство не инициализировано
 | 
						 * Записывает значение по умолчанию, если свойство не инициализировано
 | 
				
			||||||
   *
 | 
						 *
 | 
				
			||||||
   * @param string $name Название
 | 
						 * @param string $name Название
 | 
				
			||||||
   *
 | 
						 *
 | 
				
			||||||
   * @return mixed Содержимое
 | 
						 * @return mixed Содержимое
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  public function __get(string $name): mixed
 | 
						public function __get(string $name): mixed
 | 
				
			||||||
  {
 | 
						{
 | 
				
			||||||
    return match ($name) {
 | 
							return match ($name) {
 | 
				
			||||||
      'db', 'database' => $this->db ?? throw new exception("Свойство \"\$$name\" не инициализировано", 500),
 | 
								'db', 'database' => $this->db ?? throw new exception("Свойство \"\$$name\" не инициализировано", 500),
 | 
				
			||||||
      'router' => (function () {
 | 
								'router' => (function () {
 | 
				
			||||||
        // Инициализация со значением по умолчанию
 | 
									// Инициализация со значением по умолчанию
 | 
				
			||||||
        if (!$this->__isset('router')) $this->__set('router', new router);
 | 
									if (!$this->__isset('router')) $this->__set('router', new router);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Возврат (успех)
 | 
									// Возврат (успех)
 | 
				
			||||||
        return $this->router;
 | 
									return $this->router;
 | 
				
			||||||
      })(),
 | 
								})(),
 | 
				
			||||||
      'controller' => (function () {
 | 
								'controller' => (function () {
 | 
				
			||||||
        // Инициализация со значением по умолчанию
 | 
									// Инициализация со значением по умолчанию
 | 
				
			||||||
        if (!$this->__isset('controller')) $this->__set('controller', new controller);
 | 
									if (!$this->__isset('controller')) $this->__set('controller', new controller);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Возврат (успех)
 | 
									// Возврат (успех)
 | 
				
			||||||
        return $this->controller;
 | 
									return $this->controller;
 | 
				
			||||||
      })(),
 | 
								})(),
 | 
				
			||||||
      'model' => (function () {
 | 
								'model' => (function () {
 | 
				
			||||||
        // Инициализация со значением по умолчанию
 | 
									// Инициализация со значением по умолчанию
 | 
				
			||||||
        if (!$this->__isset('model')) $this->__set('model', new model);
 | 
									if (!$this->__isset('model')) $this->__set('model', new model);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Возврат (успех)
 | 
									// Возврат (успех)
 | 
				
			||||||
        return $this->model;
 | 
									return $this->model;
 | 
				
			||||||
      })(),
 | 
								})(),
 | 
				
			||||||
      'namespace' => $this->namespace ?? throw new exception("Свойство \"\$$name\" не инициализировано", 500),
 | 
								'namespace' => $this->namespace ?? throw new exception("Свойство \"\$$name\" не инициализировано", 500),
 | 
				
			||||||
      default => throw new exception("Свойство \"\$$name\" не найдено", 404)
 | 
								default => throw new exception("Свойство \"\$$name\" не найдено", 404)
 | 
				
			||||||
    };
 | 
							};
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Проверить свойство на инициализированность
 | 
						 * Проверить свойство на инициализированность
 | 
				
			||||||
   *
 | 
						 *
 | 
				
			||||||
   * @param string $name Название
 | 
						 * @param string $name Название
 | 
				
			||||||
   *
 | 
						 *
 | 
				
			||||||
   * @return bool Инициализировано свойство?
 | 
						 * @return bool Инициализировано свойство?
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  public function __isset(string $name): bool
 | 
						public function __isset(string $name): bool
 | 
				
			||||||
  {
 | 
						{
 | 
				
			||||||
    return match ($name) {
 | 
							return match ($name) {
 | 
				
			||||||
      default => isset($this->{$name})
 | 
								default => isset($this->{$name})
 | 
				
			||||||
    };
 | 
							};
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Удалить свойство
 | 
						 * Удалить свойство
 | 
				
			||||||
   *
 | 
						 *
 | 
				
			||||||
   * @param string $name Название
 | 
						 * @param string $name Название
 | 
				
			||||||
   *
 | 
						 *
 | 
				
			||||||
   * @return void
 | 
						 * @return void
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  public function __unset(string $name): void
 | 
						public function __unset(string $name): void
 | 
				
			||||||
  {
 | 
						{
 | 
				
			||||||
    match ($name) {
 | 
							match ($name) {
 | 
				
			||||||
      default => (function () use ($name) {
 | 
								default => (function () use ($name) {
 | 
				
			||||||
        // Удаление
 | 
									// Удаление
 | 
				
			||||||
        unset($this->{$name});
 | 
									unset($this->{$name});
 | 
				
			||||||
      })()
 | 
								})()
 | 
				
			||||||
    };
 | 
							};
 | 
				
			||||||
  }
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,86 +4,31 @@ declare(strict_types=1);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace mirzaev\minimal;
 | 
					namespace mirzaev\minimal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Встроенные библиотеки
 | 
					// Files of the project
 | 
				
			||||||
 | 
					use	mirzaev\minimal\traits\magic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Built-in libraries
 | 
				
			||||||
use exception;
 | 
					use exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Модель
 | 
					 * Model (base)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @package mirzaev\minimal
 | 
					 * @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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class model
 | 
					class model
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  /**
 | 
						use magic;
 | 
				
			||||||
   * Постфикс
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  public const POSTFIX = '_model';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Конструктор
 | 
						 * Postfix of file names
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  public function __construct()
 | 
						public const string POSTFIX = '_model';
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Записать свойство
 | 
						 * Constructor
 | 
				
			||||||
   *
 | 
						 */
 | 
				
			||||||
   * @param string $name Название
 | 
						public function __construct() {}
 | 
				
			||||||
   * @param mixed $value Содержимое
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @return void
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  public function __set(string $name, mixed $value = null): void
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    match ($name) {
 | 
					 | 
				
			||||||
      default => throw new exception("Свойство \"\$$name\" не найдено", 404)
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Прочитать свойство
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @param string $name Название
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @return mixed Содержимое
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  public function __get(string $name): mixed
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    return match ($name) {
 | 
					 | 
				
			||||||
      default => throw new exception("Свойство \"\$$name\" не обнаружено", 404)
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Проверить свойство на инициализированность
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @param string $name Название
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @return bool Инициализировано свойство?
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  public function __isset(string $name): bool
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    return match ($name) {
 | 
					 | 
				
			||||||
      default => isset($this->{$name})
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Удалить свойство
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @param string $name Название
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @return void
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  public function __unset(string $name): void
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    match ($name) {
 | 
					 | 
				
			||||||
      default => (function () use ($name) {
 | 
					 | 
				
			||||||
        // Удаление
 | 
					 | 
				
			||||||
        unset($this->{$name});
 | 
					 | 
				
			||||||
      })()
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,165 +11,308 @@ use mirzaev\minimal\core;
 | 
				
			|||||||
 * Маршрутизатор
 | 
					 * Маршрутизатор
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @package mirzaev\minimal
 | 
					 * @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>
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
final class router
 | 
					final class router
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * @var array $router Реестр маршрутов
 | 
						 * @var array $router Реестр маршрутов
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  protected array $routes = [];
 | 
						/* protected array $routes = []; */
 | 
				
			||||||
 | 
						public array $routes = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
						/**
 | 
				
			||||||
   * Записать маршрут
 | 
						 * Записать маршрут
 | 
				
			||||||
   *
 | 
						 *
 | 
				
			||||||
   * @param string $route Маршрут
 | 
						 * @param string $route Маршрут
 | 
				
			||||||
   * @param string $handler Обработчик - инстанции контроллера и модели (не обязательно), без постфиксов
 | 
						 * @param string $handler Обработчик - инстанции контроллера и модели (не обязательно), без постфиксов
 | 
				
			||||||
   * @param ?string $method Вызываемый метод в инстанции контроллера обработчика
 | 
						 * @param ?string $method Вызываемый метод в инстанции контроллера обработчика
 | 
				
			||||||
   * @param ?string $request HTTP-метод запроса (GET, POST, PUT...)
 | 
						 * @param ?string $request HTTP-метод запроса (GET, POST, PUT...)
 | 
				
			||||||
   * @param ?string $model Инстанция модели (переопределение инстанции модели в $target)
 | 
						 * @param ?string $model Инстанция модели (переопределение инстанции модели в $target)
 | 
				
			||||||
   *
 | 
						 * @param array $variables 
 | 
				
			||||||
   * @return void
 | 
						 *
 | 
				
			||||||
   */
 | 
						 * @return static The instance from which the method was called (fluent interface)
 | 
				
			||||||
  public function write(
 | 
						 */
 | 
				
			||||||
    string $route,
 | 
						public function write(
 | 
				
			||||||
    string $handler,
 | 
							string $route,
 | 
				
			||||||
    ?string $method = 'index',
 | 
							string $handler,
 | 
				
			||||||
    ?string $request = 'GET',
 | 
							?string $method = 'index',
 | 
				
			||||||
    ?string $model = null
 | 
							?string $request = 'GET',
 | 
				
			||||||
  ): void {
 | 
							?string $model = null,
 | 
				
			||||||
    // Запись в реестр
 | 
							array $variables = []
 | 
				
			||||||
    $this->routes[$route][$request] = [
 | 
						): static {
 | 
				
			||||||
      'controller' => $handler,
 | 
							// Запись в реестр
 | 
				
			||||||
      'model' => $model ?? $handler,
 | 
							$this->routes[$route][$request] = [
 | 
				
			||||||
      'method' => $method
 | 
								'controller' => $handler,
 | 
				
			||||||
    ];
 | 
								'model' => $model ?? $handler,
 | 
				
			||||||
  }
 | 
								'method' => $method,
 | 
				
			||||||
 | 
								'variables' => $variables
 | 
				
			||||||
 | 
							];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
							// Exit (success) (fluent interface)
 | 
				
			||||||
   * Обработать маршрут
 | 
							return $this;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Handle a request
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @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
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return string|int|null Response
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function handle(
 | 
				
			||||||
 | 
							?string $uri = null,
 | 
				
			||||||
 | 
							?string $method = null,
 | 
				
			||||||
 | 
							?core $core = new core
 | 
				
			||||||
 | 
						): string|int|null {
 | 
				
			||||||
 | 
							// Declaration of the registry of routes directoies 
 | 
				
			||||||
 | 
							$routes = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							foreach ($this->routes as $route => $data) {
 | 
				
			||||||
 | 
								// Iteration over routes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Search directories of route (explode() creates empty value in array)
 | 
				
			||||||
 | 
								preg_match_all('/(^\/$|[^\/]+)/', $route, $data['directories']);
 | 
				
			||||||
 | 
								$routes[$route] = $data['directories'][0];
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Search directories of URN (explode() creates empty value in array)
 | 
				
			||||||
 | 
								preg_match_all('/(^\/$|[^\/]+)/', $urn, $directories);
 | 
				
			||||||
 | 
								$directories = $directories[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/**
 | 
				
			||||||
 | 
								 * Initialization of the route
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Initializing the buffer of matches of route directories with URN directories
 | 
				
			||||||
 | 
								$matches = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								foreach ($directories as $i => $urn_directory) {
 | 
				
			||||||
 | 
									// Iteration over directories of URN 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									foreach ($this->routes as $route => $data) {
 | 
				
			||||||
 | 
										// Iteration over routes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if (isset($data[$method])) {
 | 
				
			||||||
 | 
											// The request method matches the route method
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											// Universalization of route
 | 
				
			||||||
 | 
											$route = self::universalize($route);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											// Skipping unmatched routes based on results of previous iterations
 | 
				
			||||||
 | 
											if (isset($matches[$route]) && $matches[$route] === false) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											// Initializing of route directory
 | 
				
			||||||
 | 
											$route_directory = $routes[$route][$i] ?? null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											if (isset($route_directory)) {
 | 
				
			||||||
 | 
												// Initialized of route directory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												if ($urn_directory === $route_directory) {
 | 
				
			||||||
 | 
													// The directory of URN is identical to the directory of route
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													// Writing: end of URN directories XNOR end of route directories
 | 
				
			||||||
 | 
													$matches[$route] = !(isset($directories[$_i = $i + 1]) xor isset($routes[$route][$_i]));
 | 
				
			||||||
 | 
												} else if (preg_match('/^\$([a-zA-Z_\x80-\xff]+)$/', $route_directory) === 1) {
 | 
				
			||||||
 | 
													// The directory of route is a variable ($variable)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													// Writing: end of URN directories XNOR end of route directories
 | 
				
			||||||
 | 
													$matches[$route] = !(isset($directories[$_i = $i + 1]) xor isset($routes[$route][$_i]));
 | 
				
			||||||
 | 
												} else if (
 | 
				
			||||||
 | 
													!isset($routes[$route][$i + 1])
 | 
				
			||||||
 | 
													&& preg_match('/^\$([a-zA-Z_\x80-\xff]+\.\.\.)$/', $route_directory) === 1
 | 
				
			||||||
 | 
												) {
 | 
				
			||||||
 | 
													// The directory of route is a collector ($variable...)
 | 
				
			||||||
 | 
													// AND this is the end of route directories
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													// Writing
 | 
				
			||||||
 | 
													$matches[$route] = 'collector';
 | 
				
			||||||
 | 
												} else $matches[$route] = false;
 | 
				
			||||||
 | 
											} else if ($matches[$route] === 'collector') {
 | 
				
			||||||
 | 
											} else $matches[$route] = false;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Finding a priority route from match results
 | 
				
			||||||
 | 
								foreach ($matches as $route => $match) if ($match !== false) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if ($route && !empty($data = $this->routes[$route])) {
 | 
				
			||||||
 | 
									// Route found
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Universalization of route
 | 
				
			||||||
 | 
									$route = self::universalize($route);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									/**
 | 
				
			||||||
 | 
									 * Initialization of route variables
 | 
				
			||||||
 | 
									 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									foreach ($routes[$route] as $i => $route_directory) {
 | 
				
			||||||
 | 
										// Iteration over directories of route
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if (preg_match('/^\$([a-zA-Z_\x80-\xff]+)$/', $route_directory) === 1) {
 | 
				
			||||||
 | 
											// The directory is a variable ($variable)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											// Запись в реестр переменных и перещапись директории в маршруте
 | 
				
			||||||
 | 
											$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 ??= [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											// Запись в реестр переменных
 | 
				
			||||||
 | 
											$collector[] = $directories[$i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											foreach (array_slice($directories, ++$i, preserve_keys: true) as &$urn_directory) {
 | 
				
			||||||
 | 
												// Перебор директорий URN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												// Запись в реестр переменных
 | 
				
			||||||
 | 
												$collector[] = $urn_directory;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											break;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									/**
 | 
				
			||||||
 | 
									 * 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'));
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Возврат (провал)
 | 
				
			||||||
 | 
							return $this->error($core);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Sorting routes
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * 1. Short routes
 | 
				
			||||||
 | 
						 * 2. Long routes
 | 
				
			||||||
 | 
						 * 3. Short routes with variables (position of variables from "right" to "left")
 | 
				
			||||||
 | 
						 * 4. Long routes with variables (position of variables from "right" to "left")
 | 
				
			||||||
 | 
						 * 5. Short routes with collector
 | 
				
			||||||
 | 
						 * 6. Long routes with collector
 | 
				
			||||||
 | 
						 * 7. Short routes with variables and collector (position of variables from "right" to "left" then by amount)
 | 
				
			||||||
 | 
						 * 8. Long routes with variables and collector (position of variables from "right" to "left")
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Добавить чтобы сначала текст потом переменная затем после переменной первыми тексты и в конце перменные опять и так рекурсивно
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return static The instance from which the method was called (fluent interface)
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
   * @param ?string $uri URI запроса (https://domain.com/foo/bar)
 | 
						 * @todo ПЕРЕДЕЛАТЬ ПОЛНОСТЬЮ
 | 
				
			||||||
   * @param ?string $method Метод запроса (GET, POST, PUT...)
 | 
						 */
 | 
				
			||||||
 | 
						public function sort(): static
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							uksort($this->routes, function (string $a, string $b) {
 | 
				
			||||||
 | 
								// Sorting routes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Initialization of string lengths (multibyte-safe)
 | 
				
			||||||
 | 
								$length = [
 | 
				
			||||||
 | 
									$a => mb_strlen($a),
 | 
				
			||||||
 | 
									$b => mb_strlen($b)
 | 
				
			||||||
 | 
								];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Initialization of the presence of variables
 | 
				
			||||||
 | 
								$variables = [
 | 
				
			||||||
 | 
									$a => preg_match('/\$([a-zA-Z_\x80-\xff]+)(\/|$)/', $a) === 1,
 | 
				
			||||||
 | 
									$b => preg_match('/\$([a-zA-Z_\x80-\xff]+)(\/|$)/', $b) === 1
 | 
				
			||||||
 | 
								];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Initialization of the presence of collectors
 | 
				
			||||||
 | 
								$collectors = [
 | 
				
			||||||
 | 
									$a => preg_match('/\$([a-zA-Z_\x80-\xff]+)\.\.\.$/', $a) === 1,
 | 
				
			||||||
 | 
									$b => preg_match('/\$([a-zA-Z_\x80-\xff]+)\.\.\.$/', $b) === 1
 | 
				
			||||||
 | 
								];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if ($variables[$a] && !$variables[$b]) return 1;
 | 
				
			||||||
 | 
								else if (!$variables[$a] && $variables[$b]) return -1;
 | 
				
			||||||
 | 
								else if ($variables[$a] && $variables[$b]) {
 | 
				
			||||||
 | 
								} else if ($collectors[$a] && !$collectors[$b]) return 1;
 | 
				
			||||||
 | 
								else if (!$collectors[$a] && $collectors[$b]) return -1;
 | 
				
			||||||
 | 
								else {
 | 
				
			||||||
 | 
									// NOR variables and XAND collectors (both are not found or both are found)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// The routes are the same length (no changes)
 | 
				
			||||||
 | 
									if ($length[$a] === $length[$b]) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// The longer route moves to the end
 | 
				
			||||||
 | 
									return $length[$a] > $length[$b] ? 1 : -1;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Exit (success) (fluent interface)
 | 
				
			||||||
 | 
							return $this;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Сгенерировать ответ с ошибкой
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Вызывает метод error404 в инстанции контроллера ошибок
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
	 * @param ?core $core Инстанция системного ядра
 | 
						 * @param ?core $core Инстанция системного ядра
 | 
				
			||||||
	 *
 | 
						 *
 | 
				
			||||||
	 * @return string|int|null Ответ
 | 
						 * @return ?string HTML-документ
 | 
				
			||||||
   */
 | 
						 */
 | 
				
			||||||
  public function handle(?string $uri = null, ?string $method = null, ?core $core = new core): string|int|null
 | 
						private function error(core $core = new core): ?string
 | 
				
			||||||
  {
 | 
						{
 | 
				
			||||||
    // Инициализация значений по умолчанию
 | 
							return class_exists($class = '\\' . $core->namespace . '\\controllers\\errors' . $core->controller::POSTFIX)
 | 
				
			||||||
    $uri ??= $_SERVER['REQUEST_URI'] ?? '/';
 | 
								&& method_exists($class, $method = 'error404')
 | 
				
			||||||
    $method ??= $_SERVER["REQUEST_METHOD"] ?? 'GET';
 | 
								? (new $class)->$method()
 | 
				
			||||||
 | 
								: null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Инициализация URL запроса (/foo/bar)
 | 
						/**
 | 
				
			||||||
    $url = parse_url($uri, PHP_URL_PATH);
 | 
						 * Universalize URN 
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
    // Универсализация маршрута
 | 
						 * Always "/" at the beginning and never "/" at the end
 | 
				
			||||||
    $url = self::universalize($url);
 | 
						 *
 | 
				
			||||||
 | 
						 * @param string &$urn URN (/foo/bar)
 | 
				
			||||||
    // Сортировка реестра маршрутов от большего ключа к меньшему (кешируется)
 | 
						 *
 | 
				
			||||||
    krsort($this->routes);
 | 
						 * @return string Universalized URN 
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
    // Поиск директорий в ссылке
 | 
						private function universalize(string $urn): string
 | 
				
			||||||
    preg_match_all('/[^\/]+/', $url, $directories);
 | 
						{
 | 
				
			||||||
 | 
							// Universalization of URN and exit (success)
 | 
				
			||||||
    // Инициализация директорий
 | 
							return (string) preg_replace('/(.+)\/$/', '$1', preg_replace('/^([^\/])/', '/$1', $urn));
 | 
				
			||||||
    $directories = $directories[0];
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
    foreach ($this->routes as $route => $data) {
 | 
					 | 
				
			||||||
      // Перебор маршрутов
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // Универсализация маршрута
 | 
					 | 
				
			||||||
      $route = self::universalize($route);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // Поиск директорий
 | 
					 | 
				
			||||||
      preg_match_all('/[^\/]+/', $route, $data['directories']);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // Инициализация директорий
 | 
					 | 
				
			||||||
      $data['directories'] = $data['directories'][0];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (count($directories) === count($data['directories'])) {
 | 
					 | 
				
			||||||
        // Входит в диапазон маршрут (совпадает количество директорий у ссылки и маршрута)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Инициализация реестра переменных
 | 
					 | 
				
			||||||
        $data['vars'] = [];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        foreach ($data['directories'] as $index => &$directory) {
 | 
					 | 
				
			||||||
          // Перебор директорий
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          if (preg_match('/\$([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]+)/', $directory) === 1) {
 | 
					 | 
				
			||||||
            // Директория является переменной (.../$variable/...)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Запись в реестр переменных
 | 
					 | 
				
			||||||
            $directory = $data['vars'][trim($directory, '$')] = $directories[$index];
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Реиницилазция маршрута
 | 
					 | 
				
			||||||
        $route = self::universalize(implode('/', $data['directories']));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Проверка на пустой маршрут
 | 
					 | 
				
			||||||
        if (empty($route)) $route = '/';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (mb_stripos($route, $url, 0, "UTF-8") === 0 && mb_strlen($route, 'UTF-8') <= mb_strlen($url, 'UTF-8')) {
 | 
					 | 
				
			||||||
          // Идентифицирован маршрут (длина не меньше длины запрошенного URL)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          if (array_key_exists($method, $data)) {
 | 
					 | 
				
			||||||
            // Идентифицирован метод маршрута (GET, POST, PUT...)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						$route = $data[$method];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (class_exists($controller = $core->namespace . '\\controllers\\' . $route['controller'] . $core->controller::POSTFIX)) {
 | 
					 | 
				
			||||||
              // Найден контроллер
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              // Инициализация инстанции ядра контроллера
 | 
					 | 
				
			||||||
							$controller = new $controller;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              // Инициализация инстанции ядра модели
 | 
					 | 
				
			||||||
              if (class_exists($model = $core->namespace . '\\models\\' . $route['model'] . $core->model::POSTFIX));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              // Вызов связанного с маршрутом методв и возврат (успех)
 | 
					 | 
				
			||||||
              return $controller->{$route['method']}($data['vars'] + $_REQUEST, $_FILES);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          // Выход из цикла (провал)
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Возврат (провал)
 | 
					 | 
				
			||||||
    return $this->error($core);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Сгенерировать ответ с ошибкой
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * Вызывает метод 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;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Универсализировать маршрут 
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @param string $route Маршрут
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @return string Универсализированный маршрут
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  private function universalize(string $route): string
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    // Если не записан "/" в начале, то записать, затем, если записан "/" в конце, то удалить
 | 
					 | 
				
			||||||
    return preg_replace('/(.+)\/$/', '$1', preg_replace('/^([^\/])/', '/$1', $route));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										81
									
								
								mirzaev/minimal/system/traits/magic.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										81
									
								
								mirzaev/minimal/system/traits/magic.php
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace mirzaev\minimal\traits;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Built-in libraries
 | 
				
			||||||
 | 
					use exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Trait of magical methods
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @method void __set(string $name, mixed $value = null) 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
 | 
				
			||||||
 | 
					 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					trait magic
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 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) {
 | 
				
			||||||
 | 
								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) {
 | 
				
			||||||
 | 
								default => throw new exception('Property "' . static::class . "::\$$name\" not found", 404)
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Delete property
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param string $name Name of the property
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return void
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function __unset(string $name): void
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							match ($name) {
 | 
				
			||||||
 | 
								default => (function () use ($name) {
 | 
				
			||||||
 | 
									unset($this->{$name});
 | 
				
			||||||
 | 
								})()
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Check property for initialization
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param string $name Name of the property
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @return bool Is the property initialized?
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function __isset(string $name): bool
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return match ($name) {
 | 
				
			||||||
 | 
								default => isset($this->{$name})
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user