使用Composer从零开发一个简单的restful框架(09)-全局中间件

路由

编辑core/Route.php,添加中间件相关成员变量和修改相关方法

private array $routes = []; //分组下的路由, 包括分组
private array $middlewares; //分组中间件


public function __construct(string $group, string $nsParent = '', array $middlewares = []) {
    $this->group = $group;
    $this->nsParent = $nsParent;
    $this->middlewares = $middlewares;
}
public function addChild(Route $child): void {
    $this->routes[] = $child;
}

public function getMiddlewares(): array {
    return $this->middlewares;
}

public function addMiddleware(array | string $middleware): void {
    if (is_array($middleware)) {
        $this->middlewares = array_merge($this->middlewares, $middleware);
    } else {
        $this->middlewares[] = $middleware;
    }
}

编辑core/Router.php,修改和添加中间件相关方法

public function __construct() {
    // 全局中间件放到根路由
    $route = new Route('/', '', APP_MIDDLEWARES);

    $this->curr = $route;

    // 加载路由文件(路由文件使用 $router 访问)
    $router = $this;

    foreach (glob(PATH_ROUTE . '*.php') as $routeFile) {
        require_once $routeFile;
    }
}


// 开始路由处理
public function routing(Request $request, Response $response): void {
    $uri = $request->getUri();
    slash_add_end($uri);

    $this->dispatch($this->curr, $request->getMethod(), $uri, $request, $response);

    $response->send();
}


// 路由分组
public function group(string $group, \Closure $cb): void {
    $lastRoute = $this->curr;

    slash_add_head($group);
    $route = new Route($group, $lastRoute->getNamespace(), $lastRoute->getMiddlewares());
    $this->curr->addChild($route);
    $this->curr = $route;

    call_user_func($cb, $this);

    $this->curr = $lastRoute;
}

// 设置命名空间
public function namespace(string $ns): void {
    $this->curr->setNamespace($ns);
}

// 设置中间件
public function middleware(array | string $middleware): void {
    $this->curr->addMiddleware($middleware);
}

编辑core/Router.php,修改dispatch方法

private function dispatch(Route $route, string $method, string $uri, Request $request, Response $response): void {
    $group = $route->getGroup();

    // 分组不会有命名参数, 不包含直接返回
    if (!str_contains($uri, $group)) return;

    $uri = substr($uri, strlen($group));
    slash_add_head($uri);

    $routes = $route->getRoutes();

    foreach ($routes as $item) {
        if ($item instanceof RouteItem) {
            if ($method == $item->method && $uri == $item->uri) {
                // 全匹配路由
                $this->applyMiddleware($route->getMiddlewares(), $item, $route->getNamespace(), $request, $response);

                return;
            } else if ($method == $item->method  && preg_match_all('(:\w+)', $item->uri, $names)) {
                // 参数路由
                $pattern = preg_replace('(:\w+)', '(\w+)', $item->uri);
                if (preg_match('#^' . $pattern . '$#', $uri, $matches)) {
                    $params = $this->mergeParams($names[0], $matches);
                    $request->setParams($params);

                    $this->applyMiddleware($route->getMiddlewares(), $item, $route->getNamespace(), $request, $response);

                    return;
                }
            }
        } else {
            $this->dispatch($item, $method, $uri, $request, $response);
        }
    }
}

private function applyMiddleware(array $middlewares, RouteItem $item, string $namespace, Request $request, Response $response): void {
    if (!empty($middlewares)) {
        $middleware = array_shift($middlewares);
        $next = function () use ($middlewares, $item, $namespace, $request, $response) {
            $this->applyMiddleware($middlewares, $item, $namespace, $request, $response);
        };
        call_user_func_array([new $middleware(), 'handle'], [$request, $response, $next]);
    } else {
        $this->start($item, $namespace, $request, $response);
    }
}

应用

新建 app/middleware/All.php,内容如下

<?php
namespace app\middleware;

use core\Request;
use core\Response;


class All {
    public function handle(Request $request, Response $response, \Closure $next): void {
        echo 'All(全局) 中间件 call before<br>';
        $next();
        echo 'All(全局) 中间件 call after<br>';
    }
}

编辑public/index.php,设置全局中间件

// 应用控制器命名空间
const APP_CONTROLLER_NAMESPACE = 'app\controller';
// 全局中间件
const APP_MIDDLEWARES = [\app\middleware\All::class];

// 根目录
const PATH_ROOT = __DIR__ . '/../';

测试

浏览器访问任意可以访问的链接,前面两行都会输出

All(全局) 中间件 call before
All(全局) 中间件 call after
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!