使用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_starts_with($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 协议》,转载必须注明作者和本文链接