Przeglądaj źródła

added middlewares

Плотников Роман Вячеславович 3 lat temu
rodzic
commit
13d7120728

+ 17 - 0
app/Middlewares/AuthMiddleware.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace Middlewares;
+
+use Src\Auth\Auth;
+use Src\Request;
+
+class AuthMiddleware
+{
+    public function handle(Request $request)
+    {
+        //Если пользователь не авторизован, то редирект на страницу входа
+        if (!Auth::check()) {
+            app()->route->redirect('/login');
+        }
+    }
+}

+ 2 - 1
composer.json

@@ -12,7 +12,8 @@
     "require": {
         "php": "^7.4 | ^8.0",
         "illuminate/database": "10.x-dev",
-        "illuminate/events": "10.x-dev"
+        "illuminate/events": "10.x-dev",
+        "nikic/fast-route": "2.0.x-dev"
     },
     "autoload": {
         "psr-4": {

+ 5 - 1
config/app.php

@@ -3,5 +3,9 @@ return [
    //Класс аутентификации
    'auth' => \Src\Auth\Auth::class,
    //Клас пользователя
-   'identity'=>\Model\User::class
+   'identity' => \Model\User::class,
+   //Классы для middleware
+   'routeMiddleware' => [
+       'auth' => \Middlewares\AuthMiddleware::class,
+   ]
 ];

+ 2 - 1
core/Src/Application.php

@@ -19,7 +19,7 @@ class Application
         //Привязываем класс со всеми настройками приложения
         $this->settings = $settings;
         //Привязываем класс маршрутизации с установкой префикса
-        $this->route = new Route($this->settings->getRootPath());
+        $this->route = Route::single()->setPrefix($this->settings->getRootPath());
         //Создаем класс менеджера для базы данных
         $this->dbManager = new Capsule();
         //Создаем класс для аутентификации на основе настроек приложения
@@ -31,6 +31,7 @@ class Application
         $this->auth::init(new $this->settings->app['identity']);
     }
 
+
     public function __get($key)
     {
         switch ($key) {

+ 56 - 0
core/Src/Middleware.php

@@ -0,0 +1,56 @@
+<?php
+
+namespace Src;
+
+use FastRoute\RouteCollector;
+use FastRoute\RouteParser\Std;
+use FastRoute\DataGenerator\MarkBased;
+use FastRoute\Dispatcher\MarkBased as Dispatcher;
+use Src\Traits\SingletonTrait;
+
+class Middleware
+{
+    //Используем трейт
+    use SingletonTrait;
+
+    private RouteCollector $middlewareCollector;
+
+    public function add($httpMethod, string $route, array $action): void
+    {
+        $this->middlewareCollector->addRoute($httpMethod, $route, $action);
+    }
+    public function group(string $prefix, callable $callback): void
+    {
+        $this->middlewareCollector->addGroup($prefix, $callback);
+    }
+
+    //Конструктор скрыт. Вызывается только один раз
+    private function __construct()
+    {
+        $this->middlewareCollector = new RouteCollector(new Std(), new MarkBased());
+    }
+
+    //Запуск всех middlewares для текущего маршрута
+    public function runMiddlewares(string $httpMethod, string $uri): Request
+    {
+        $request = new Request();
+        //Получаем список всех разрешенных классов middlewares из настроек приложения
+        $routeMiddleware = app()->settings->app['routeMiddleware'];
+
+        //Перебираем все middlewares для текущего адреса
+        foreach ($this->getMiddlewaresForRoute($httpMethod, $uri) as $middleware) {
+            $args = explode(':', $middleware);
+            //Создаем объект и вызываем метод handle
+            (new $routeMiddleware[$args[0]])->handle($request, $args[1] ?? null);
+        }
+        //Возвращаем итоговый request
+        return $request;
+    }
+
+    //Поиск middlewares по адресу
+    private function getMiddlewaresForRoute(string $httpMethod, string $uri): array
+    {
+        $dispatcherMiddleware = new Dispatcher($this->middlewareCollector->getData());
+        return $dispatcherMiddleware->dispatch($httpMethod, $uri)[1] ?? [];
+    }
+}

+ 68 - 27
core/Src/Route.php

@@ -4,59 +4,100 @@ namespace Src;
 
 use Error;
 
+use FastRoute\RouteCollector;
+use FastRoute\RouteParser\Std;
+use FastRoute\DataGenerator\MarkBased;
+use FastRoute\Dispatcher\MarkBased as Dispatcher;
+use Src\Traits\SingletonTrait;
+
 class Route
 {
-    private static array $routes = [];
-    private static string $prefix = '';
+    //Используем методы трейта
+    use SingletonTrait;
+
+    //Свойство для хранения текущего маршрута
+    private string $currentRoute = '';
+    private $currentHttpMethod;
+
+    //Свойство для префикса для всех маршрутов
+    private string $prefix = '';
+
+    //Классы для использования внешнего маршрутизатора
+    private RouteCollector $routeCollector;
 
-    public static function setPrefix($value)
+    //Добавляет маршрут, устанавливает его текущим и возвращает объект
+    public static function add($httpMethod, string $route, array $action): self
     {
-        self::$prefix = $value;
+        self::single()->routeCollector->addRoute($httpMethod, $route, $action);
+        self::single()->currentHttpMethod = $httpMethod;
+        self::single()->currentRoute = $route;
+        return self::single();
     }
 
-    public static function add(string $route, array $action): void
+    //Добавляет префикс для обозначенных маршрутов
+    public static function group(string $prefix, callable $callback): void
     {
-        if (!array_key_exists($route, self::$routes)) {
-            self::$routes[$route] = $action;
-        }
+        self::single()->routeCollector->addGroup($prefix, $callback);
+        Middleware::single()->group($prefix, $callback);
+    }
+
+    //Конструктор скрыт. Вызывается только один раз
+    private function __construct()
+    {
+        $this->routeCollector = new RouteCollector(new Std(), new MarkBased());
+    }
+
+    public function setPrefix(string $value = ''): self
+    {
+        $this->prefix = $value;
+        return $this;
     }
 
     public function redirect(string $url): void
     {
         header('Location: ' . $this->getUrl($url));
     }
-
     public function getUrl(string $url): string
     {
-        return self::$prefix . $url;
+        return $this->prefix . $url;
     }
 
-    public function __construct(string $prefix = '')
+    //Добавление middlewares для текущего маршрута
+    public function middleware(...$middlewares): self
     {
-        self::setPrefix($prefix);
+        Middleware::single()->add($this->currentHttpMethod, $this->currentRoute, $middlewares);
+        return $this;
     }
 
-
     public function start(): void
     {
-        $path = explode('?', $_SERVER['REQUEST_URI'])[0];
-        $path = substr($path, strlen(self::$prefix) + 1);
+        // Fetch method and URI from somewhere
+        $httpMethod = $_SERVER['REQUEST_METHOD'];
+        $uri = $_SERVER['REQUEST_URI'];
 
-        if (!array_key_exists($path, self::$routes)) {
-            throw new Error('Path does not exist');
+        // Strip query string (?foo=bar) and decode URI
+        if (false !== $pos = strpos($uri, '?')) {
+            $uri = substr($uri, 0, $pos);
         }
+        $uri = rawurldecode($uri);
+        $uri = substr($uri, strlen($this->prefix));
 
-        $class = self::$routes[$path][0];
-        $action = self::$routes[$path][1];
-
-        if (!class_exists($class)) {
-            throw new Error('Class does not exist');
-        }
+        $dispatcher = new Dispatcher($this->routeCollector->getData());
 
-        if (!method_exists($class, $action)) {
-            throw new Error('Method does not exist');
+        $routeInfo = $dispatcher->dispatch($httpMethod, $uri);
+        switch ($routeInfo[0]) {
+            case Dispatcher::NOT_FOUND:
+                throw new Error('NOT_FOUND');
+            case Dispatcher::METHOD_NOT_ALLOWED:
+                throw new Error('METHOD_NOT_ALLOWED');
+            case Dispatcher::FOUND:
+                $handler = $routeInfo[1];
+                $vars = array_values($routeInfo[2]);
+                $vars[] = Middleware::single()->runMiddlewares($httpMethod, $uri);
+                $class = $handler[0];
+                $action = $handler[1];
+                call_user_func([new $class, $action], ...$vars);
+                break;
         }
-
-        call_user_func([new $class, $action], new Request());
     }
 }

+ 19 - 0
core/Src/Traits/SingletonTrait.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Src\Traits;
+
+//Позволяет для любого класса организовать паттерн Одиночка
+trait SingletonTrait
+{
+   private static self $instance;
+
+   public static function single(): self
+   {
+       if (empty(self::$instance)) self::$instance = new static();
+       return self::$instance;
+   }
+
+   private function __construct()
+   {
+   }
+}

+ 5 - 5
routes/web.php

@@ -2,8 +2,8 @@
 
 use Src\Route;
 
-Route::add('go', [Controller\Site::class, 'index']);
-Route::add('hello', [Controller\Site::class, 'hello']);
-Route::add('signup', [Controller\Site::class, 'signup']);
-Route::add('login', [Controller\Site::class, 'login']);
-Route::add('logout', [Controller\Site::class, 'logout']);
+Route::add('GET', '/hello', [Controller\Site::class, 'hello'])
+    ->middleware('auth');
+Route::add(['GET', 'POST'], '/signup', [Controller\Site::class, 'signup']);
+Route::add(['GET', 'POST'], '/login', [Controller\Site::class, 'login']);
+Route::add('GET', '/logout', [Controller\Site::class, 'logout']);