From a39a272deeb6da5c46b7acdf9d185600e29935d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=83=B3=E6=89=93=E7=9E=8C=E7=9D=A1?= Date: Fri, 12 Jul 2024 15:44:17 +0900 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84hyperf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vitepress/sidebar.ts | 3 - src/hyperf/index.md | 35 +++- src/hyperf/中间件.md | 21 --- src/hyperf/响应.md | 327 +++++++++++++++++++++++++++++++++++-- src/hyperf/接收请求.md | 266 +++++++++++++++++++++++++++++- src/hyperf/路由寻址.md | 26 --- src/hyperf/阅读版本.md | 34 ---- 7 files changed, 608 insertions(+), 104 deletions(-) delete mode 100644 src/hyperf/中间件.md delete mode 100644 src/hyperf/路由寻址.md delete mode 100644 src/hyperf/阅读版本.md diff --git a/.vitepress/sidebar.ts b/.vitepress/sidebar.ts index 03ed7b0..5982525 100644 --- a/.vitepress/sidebar.ts +++ b/.vitepress/sidebar.ts @@ -7,14 +7,11 @@ export default [ text: 'Hyperf', collapsed: true, items: [ - { text: '阅读版本', link: hyperf + '阅读版本' }, { text: '入口文件', link: hyperf + '入口文件' }, { text: '初始化依赖注入 (DI) 容器', link: hyperf + 'DI' }, { text: '初始化容器类', link: hyperf + 'container' }, { text: '启动服务', link: hyperf + '启动服务' }, { text: '接收请求', link: hyperf + '接收请求' }, - { text: '路由寻址', link: hyperf + '路由寻址' }, - { text: '中间件', link: hyperf + '中间件' }, { text: '响应', link: hyperf + '响应' }, { text: '附录1 container的get方法', link: hyperf + '附录1 container的get方法' }, { text: '附录2 注解命令获取', link: hyperf + '附录2 注解命令获取' }, diff --git a/src/hyperf/index.md b/src/hyperf/index.md index f444e19..14f4750 100644 --- a/src/hyperf/index.md +++ b/src/hyperf/index.md @@ -1 +1,34 @@ -hhhh \ No newline at end of file +--- +title: 版本记录 +--- + +> 记录阅读代码版本 + +### docker镜像 +::: tip +基于`hyperf/hyperf:8.1-alpine-v3.18-swoole`镜像环境 +::: +### php版本 + +::: tip +`php`版本要求`>=8.1.0` +::: + +### hyperf版本 + +::: tip +`hyperf`版本`~3.1.0` +::: + +### 启动命令 +在命令行中输入以下命令启动项目 +```bash +docker run --name hyperf \ +-v $PWD/skeleton:/data/project \ +-w /data/project \ +-p 9501:9501 -it \ +--privileged -u root \ +--entrypoint /bin/sh \ +hyperf/hyperf:8.1-alpine-v3.18-swoole +# 其中 $PWD/skeleton可替换为自己的目录 +``` diff --git a/src/hyperf/中间件.md b/src/hyperf/中间件.md deleted file mode 100644 index 13f6761..0000000 --- a/src/hyperf/中间件.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: 中间件 ---- - -#### getAttribute方法 -上面将匹配的路由信息放到`attribute`属性中,现在通过`getAttribute`方法获取。 -```php -$dispatched = $psr7Request->getAttribute(Dispatched::class); -// 获取中间件 -$middlewares = $this->middlewares; -$registeredMiddlewares = []; -// 如果匹配到路由,则获取路由定义的中间件 -if ($dispatched->isFound()) { - $registeredMiddlewares = MiddlewareManager::get($this->serverName, $dispatched->handler->route, $psr7Request->getMethod()); - $middlewares = array_merge($middlewares, $registeredMiddlewares); -} -// 对路由进行排序 -if ($this->option?->isMustSortMiddlewares() || $registeredMiddlewares) { - $middlewares = MiddlewareManager::sortMiddlewares($middlewares); -} -``` \ No newline at end of file diff --git a/src/hyperf/响应.md b/src/hyperf/响应.md index 99031bc..40e8393 100644 --- a/src/hyperf/响应.md +++ b/src/hyperf/响应.md @@ -2,49 +2,140 @@ title: 响应 --- +# 响应 + +最后一步,获取响应。 + ```php -// $this->dispatcher 是实例化时传入的HttpDispatcher对象 -$psr7Response = $this->dispatcher->dispatch($psr7Request, $middlewares, $this->coreMiddleware); +public function onRequest($request, $response): void +{ + try { + CoordinatorManager::until(Constants::WORKER_START)->yield(); + [$psr7Request, $psr7Response] = $this->initRequestAndResponse($request, $response); + $psr7Request = $this->coreMiddleware->dispatch($psr7Request); + + $this->option?->isEnableRequestLifecycle() && $this->event?->dispatch(new RequestReceived( + request: $psr7Request, + response: $psr7Response, + server: $this->serverName + )); + + /** @var Dispatched $dispatched */ + $dispatched = $psr7Request->getAttribute(Dispatched::class); + $middlewares = $this->middlewares; + + $registeredMiddlewares = []; + if ($dispatched->isFound()) { + $registeredMiddlewares = MiddlewareManager::get($this->serverName, $dispatched->handler->route, $psr7Request->getMethod()); + $middlewares = array_merge($middlewares, $registeredMiddlewares); + } + + if ($this->option?->isMustSortMiddlewares() || $registeredMiddlewares) { + $middlewares = MiddlewareManager::sortMiddlewares($middlewares); + } + + $psr7Response = $this->dispatcher->dispatch($psr7Request, $middlewares, $this->coreMiddleware); // [!code focus] + } catch (Throwable $throwable) { + // Delegate the exception to exception handler. + $psr7Response = $this->container->get(SafeCaller::class)->call(function () use ($throwable) { + return $this->exceptionHandlerDispatcher->dispatch($throwable, $this->exceptionHandlers); + }, static function () { + return (new Psr7Response())->withStatus(400); + }); + } finally { + if (isset($psr7Request) && $this->option?->isEnableRequestLifecycle()) { + defer(fn () => $this->event?->dispatch(new RequestTerminated( + request: $psr7Request, + response: $psr7Response ?? null, + exception: $throwable ?? null, + server: $this->serverName + ))); + + $this->event?->dispatch(new RequestHandled( + request: $psr7Request, + response: $psr7Response ?? null, + exception: $throwable ?? null, + server: $this->serverName + )); + } + + // Send the Response to client. + if (! isset($psr7Response) || ! $psr7Response instanceof ResponseInterface) { + return; + } + if (isset($psr7Request) && $psr7Request->getMethod() === 'HEAD') { + $this->responseEmitter->emit($psr7Response, $response, false); + } else { + $this->responseEmitter->emit($psr7Response, $response); + } + } +} ``` + +`$this->dispatcher`调度器是在注册回调事件的时候实例化赋值得到的。 + +```php +public function __construct( + protected ContainerInterface $container, + protected HttpDispatcher $dispatcher, // [!code focus] + protected ExceptionHandlerDispatcher $exceptionHandlerDispatcher, + protected ResponseEmitter $responseEmitter +) { + if ($this->container->has(EventDispatcherInterface::class)) { + $this->event = $this->container->get(EventDispatcherInterface::class); + } +} +``` + `$this->coreMiddleware`传入的是一个`CoreMiddleware`对象, `$middlewares`传入的是配置的中间件数组。 > 文件位置: /vendor/hyperf/dispatcher/src/HttpDispatcher.php + ```php public function dispatch(...$params): ResponseInterface { /** + * 从参数中解析对应的三个变量(请求对象,中间件数组,核心 * @param RequestInterface $request * @param array $middlewares * @param MiddlewareInterface $coreHandler */ [$request, $middlewares, $coreHandler] = $params; - // 实例化 + // 实例化HttpRequestHandler处理器 $requestHandler = new HttpRequestHandler($middlewares, $coreHandler, $this->container); return $requestHandler->handle($request); } ``` + `dispatch`方法实例化`HttpRequestHandler`对象,并调用`handle`方法。 -### HttpRequestHandler类 +### 处理请求 + ```php $requestHandler = new HttpRequestHandler($middlewares, $coreHandler, $this->container); ``` + 该类没有构造方法,在父类中定义了`__construct`方法,接收三个参数。 + ```php public function __construct(protected array $middlewares, protected $coreHandler, protected ContainerInterface $container) { // 将中间件数组的value取出 $this->middlewares = array_values($this->middlewares); } ``` + 实例化后调用`handle`方法。 + ```php public function handle(ServerRequestInterface $request): ResponseInterface { return $this->handleRequest($request); } ``` + `handleRequest`方法,这里执行所有的中间件,全部执行完成之后,会执行`CoreMiddleware`对象中的`process`方法。 + ```php protected function handleRequest($request) { @@ -71,8 +162,42 @@ protected function next(): self } ``` -#### CoreMiddleware的process方法 +当执行完一个中间件后,最后会返回`return $handler->handle($request);`,而`$handler`参数依旧是`HttpRequestHandler`对象。所以会将中间件以此调用对应的`process`方法,直到配置的中间件全部执行完成,然后调用`CoresMiddleware`中间件的`process`方法。 +::: details 参考中间件 + +```php +class CorsMiddleware implements MiddlewareInterface +{ + public function __construct(protected ContainerInterface $container) + { + } + + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + $response = Context::get(ResponseInterface::class); + $response = $response->withHeader('Access-Control-Allow-Origin', '*') + ->withHeader('Access-Control-Allow-Credentials', 'true') + // Headers 可以根据实际情况进行改写。 + ->withHeader('Access-Control-Allow-Headers', 'DNT,Keep-Alive,User-Agent,Cache-Control,Content-Type,Authorization'); + + Context::set(ResponseInterface::class, $response); + + if ($request->getMethod() == 'OPTIONS') { + return $response; + } + return $handler->handle($request); // [!code focus] + } +} +``` + +::: + +#### 核心中间件的process方法 +> > 文件位置: /vendor/hyperf/http-server/src/CoreMiddleware.php + +该方法根据路由信息,获取响应内容, + ```php public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { @@ -102,9 +227,36 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface } ``` -当匹配到路由时,执行`handleFound`方法。 +##### 路由不存在 + +抛出`NotFoundException`异常。 + +```php +protected function handleNotFound(ServerRequestInterface $request): mixed +{ + throw new NotFoundHttpException(); +} +``` + +##### 方法不允许 + +抛出`MethodNotAllowedHttpException`异常。 + +```php +protected function handleMethodNotAllowed(array $methods, ServerRequestInterface $request): mixed +{ + throw new MethodNotAllowedHttpException('Allow: ' . implode(', ', $methods)); +} +``` + +##### 匹配成功 + `dispatched`对象结构如下, + ![]( https://oss.xiaokeaii.top/2024/dispatched.png) + +如果匹配成功,则会执行`handleFound`方法,去执行路由中定义的方法或者闭包。 + ```php protected function handleFound(Dispatched $dispatched, ServerRequestInterface $request): mixed { @@ -130,8 +282,18 @@ protected function handleFound(Dispatched $dispatched, ServerRequestInterface $r return $response; } ``` -##### 控制器类型分析 -`prepareHandler`方法解析出控制器类和控制器方法。 + +###### 控制器类型分析 + +`prepareHandler`方法解析出控制器类和控制器方法。根据解析方式,可以使用四种定义方式, + +```php +Router::get('/array', [LoginController::class, 'chunk']); +Router::get('/str', 'App\Controller\StrController@index'); +Router::get('/str2', 'App\Controller\StrController::index'); +Router::get('/invoke', 'App\Controller\StrController'); +``` + ```php protected function prepareHandler(string|array $handler): array { @@ -150,22 +312,32 @@ protected function prepareHandler(string|array $handler): array throw new RuntimeException('Handler not exist.'); } ``` -解析出控制器实例。 + +解析出控制器实例,执行控制器方法。 + ```php $controllerInstance = $this->container->get($controller); +$parameters = $this->parseMethodParameters($controller, $action, $dispatched->params); +// 调用控制器方法 +$response = $controllerInstance->{$action}(...$parameters) ``` -##### 闭包类型分析 +###### 闭包类型分析 + 解析闭包参数,调用闭包,返回响应。 + ```php $parameters = $this->parseClosureParameters($dispatched->handler->callback, $dispatched->params); $callback = $dispatched->handler->callback; $response = $callback(...$parameters); ``` -`handleFound`方法走完,获取到响应对象。 +`handleFound`方法走完,获取到响应信息。 + ##### 转换响应对象 + 如果响应对象不是`ResponsePlusInterface`接口的实现类,需要进行转换。 + ```php if (! $response instanceof ResponsePlusInterface) { $response = $this->transferToResponse($response, $request); @@ -210,7 +382,9 @@ protected function transferToResponse($response, ServerRequestInterface $request 返回响应对象。 -### 发送响应到客户端 +## 发送响应到客户端 + +前面已经完成对请求的处理工作,接下来需要将响应信息返回给客户端。 ```php if (isset($psr7Request) && $psr7Request->getMethod() === 'HEAD') { @@ -219,8 +393,11 @@ if (isset($psr7Request) && $psr7Request->getMethod() === 'HEAD') { $this->responseEmitter->emit($psr7Response, $response); } ``` -#### emit方法 + +### emit方法 + 发送响应到客户端。 + ```php public function emit(ResponseInterface $response, mixed $connection, bool $withContent = true): void { @@ -248,4 +425,128 @@ public function emit(ResponseInterface $response, mixed $connection, bool $withC $this->logger?->critical((string) $exception); } } +``` + +##### 将响应转换为swoole的响应对象 + +```php +protected function buildSwooleResponse(Response $swooleResponse, ResponseInterface $response): void +{ + // Headers + foreach ($response->getHeaders() as $key => $value) { + $swooleResponse->header($key, $value); + } + + // Cookies + // This part maybe only supports of hyperf/http-message component. + if (method_exists($response, 'getCookies')) { + foreach ((array) $response->getCookies() as $domain => $paths) { + foreach ($paths ?? [] as $path => $item) { + foreach ($item ?? [] as $name => $cookie) { + if ($this->isMethodsExists($cookie, [ + 'isRaw', 'getValue', 'getName', 'getExpiresTime', 'getPath', 'getDomain', 'isSecure', 'isHttpOnly', 'getSameSite', + ])) { + $value = $cookie->isRaw() ? $cookie->getValue() : rawurlencode($cookie->getValue()); + $swooleResponse->rawcookie($cookie->getName(), $value, $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly(), (string) $cookie->getSameSite()); + } + } + } + } + } + + // Trailers + if (method_exists($response, 'getTrailers') && method_exists($swooleResponse, 'trailer')) { + foreach ($response->getTrailers() ?? [] as $key => $value) { + $swooleResponse->trailer($key, $value); + } + } + + // Status code + $swooleResponse->status($response->getStatusCode(), $response->getReasonPhrase()); +} +``` + +## 异常捕获 + +当在代码中出现异常时,会被`catch`住,然后通过`SafeCaller`类来调用配置的异常处理器。 +:::code-group +```php +try { + CoordinatorManager::until(Constants::WORKER_START)->yield(); + [$psr7Request, $psr7Response] = $this->initRequestAndResponse($request, $response); + $psr7Request = $this->coreMiddleware->dispatch($psr7Request); + + $this->option?->isEnableRequestLifecycle() && $this->event?->dispatch(new RequestReceived( + request: $psr7Request, + response: $psr7Response, + server: $this->serverName + )); + + /** @var Dispatched $dispatched */ + $dispatched = $psr7Request->getAttribute(Dispatched::class); + $middlewares = $this->middlewares; + + $registeredMiddlewares = []; + if ($dispatched->isFound()) { + $registeredMiddlewares = MiddlewareManager::get($this->serverName, $dispatched->handler->route, $psr7Request->getMethod()); + $middlewares = array_merge($middlewares, $registeredMiddlewares); + } + + if ($this->option?->isMustSortMiddlewares() || $registeredMiddlewares) { + $middlewares = MiddlewareManager::sortMiddlewares($middlewares); + } + + $psr7Response = $this->dispatcher->dispatch($psr7Request, $middlewares, $this->coreMiddleware); +} catch (Throwable $throwable) { // [!code focus:7] + // Delegate the exception to exception handler. + $psr7Response = $this->container->get(SafeCaller::class)->call(function () use ($throwable) { + return $this->exceptionHandlerDispatcher->dispatch($throwable, $this->exceptionHandlers); + }, static function () { + return (new Psr7Response())->withStatus(400); + }); +} finally { + if (isset($psr7Request) && $this->option?->isEnableRequestLifecycle()) { + defer(fn () => $this->event?->dispatch(new RequestTerminated( + request: $psr7Request, + response: $psr7Response ?? null, + exception: $throwable ?? null, + server: $this->serverName + ))); + + $this->event?->dispatch(new RequestHandled( + request: $psr7Request, + response: $psr7Response ?? null, + exception: $throwable ?? null, + server: $this->serverName + )); + } + + // Send the Response to client. + if (! isset($psr7Response) || ! $psr7Response instanceof ResponseInterface) { + return; + } + if (isset($psr7Request) && $psr7Request->getMethod() === 'HEAD') { + $this->responseEmitter->emit($psr7Response, $response, false); + } else { + $this->responseEmitter->emit($psr7Response, $response); + } +} +``` +::: + +`call`方法会执行闭包里的内容,如果执行闭包发生错误,则捕获异常并输出日志,返回默认400状态码。 +```php + +public function call(Closure $closure, ?Closure $default = null, string $level = LogLevel::CRITICAL): mixed +{ + try { + return $closure(); + } catch (Throwable $exception) { + if ($this->container->has(StdoutLoggerInterface::class) && $logger = $this->container->get(StdoutLoggerInterface::class)) { + $logger->log($level, (string) $exception); + } + } + + return value($default); +} ``` \ No newline at end of file diff --git a/src/hyperf/接收请求.md b/src/hyperf/接收请求.md index 20259ad..e34b57a 100644 --- a/src/hyperf/接收请求.md +++ b/src/hyperf/接收请求.md @@ -1,6 +1,10 @@ --- title: 接收请求 --- +::: tip +请参考[附录5](./附录5%20初始化中间件){target="_blank"}之后,再来看这篇。 +::: + # 接收请求 由前一节可知,事件的注册在`Hyerf\Server\Server`类的`registerSwooleEvents`方法中完成。 @@ -20,6 +24,7 @@ title: 接收请求 ::: tip 要理解上面事件,请参考`Swoole`的运行流程。 ::: + ### Swoole流程 首先了解`Swoole`的运行流程,(图源网络) @@ -29,16 +34,20 @@ title: 接收请求 ::: tip 当请求过来时,会触发`Request`事件,基于之前准备阶段注册的`swoole`事件可知,会调用`Hyperf\HttpServer\Server`类的`onRequest`函数。 ::: + ### OnRequest方法 ```php public function onRequest($request, $response): void { try { + // 等待worker进程启动 CoordinatorManager::until(Constants::WORKER_START)->yield(); - + // 请求和响应对象转换为符合 PSR-7标准的对象($request, $response为swoole返回的对象) [$psr7Request, $psr7Response] = $this->initRequestAndResponse($request, $response); + // 路由匹配 $psr7Request = $this->coreMiddleware->dispatch($psr7Request); + $this->option?->isEnableRequestLifecycle() && $this->event?->dispatch(new RequestReceived( request: $psr7Request, response: $psr7Response, @@ -46,18 +55,24 @@ public function onRequest($request, $response): void )); /** @var Dispatched $dispatched */ + // 获取匹配的路由信息 $dispatched = $psr7Request->getAttribute(Dispatched::class); + // 获取配置文件中定义的路由 $middlewares = $this->middlewares; $registeredMiddlewares = []; + // 路由存在的情况 if ($dispatched->isFound()) { + // 获取路由注册的中间件 $registeredMiddlewares = MiddlewareManager::get($this->serverName, $dispatched->handler->route, $psr7Request->getMethod()); + // 跟配置文件合并 $middlewares = array_merge($middlewares, $registeredMiddlewares); } - + // 如果mustSortMiddlewares参数为true或者registeredMiddlewares中间件存在,则进行排序操作 if ($this->option?->isMustSortMiddlewares() || $registeredMiddlewares) { $middlewares = MiddlewareManager::sortMiddlewares($middlewares); } + // 获取响应信息 $psr7Response = $this->dispatcher->dispatch($psr7Request, $middlewares, $this->coreMiddleware); } catch (Throwable $throwable) { // Delegate the exception to exception handler. @@ -98,21 +113,260 @@ public function onRequest($request, $response): void } ``` -#### CoordinatorManager类 `CoordinatorManager::until`用于创建一个协程等待器,指示协程等待某个事件的完成, 这里是等待`worker`进程启动完成。 + ```php CoordinatorManager::until(Constants::WORKER_START)->yield(); ``` + 确保在处理请求之前,所有的`Worker`进程都已经启动完成,以保证后续的操作能够顺利执行。 -#### initRequestAndResponse方法 -将请求和响应对象转换为符合 `PSR-7`标准的对象。 -```php +:::code-group + +```php [转换为标准对象] [$psr7Request, $psr7Response] = $this->initRequestAndResponse($request, $response); ``` +```php [转换实现] +protected function initRequestAndResponse($request, $response): array +{ + // 设置到`ResponseContext`中, + ResponseContext::set($psr7Response = new Psr7Response()); + + $psr7Response->setConnection(new WritableConnection($response)); + + if ($request instanceof ServerRequestInterface) { + $psr7Request = $request; + } else { + $psr7Request = Psr7Request::loadFromSwooleRequest($request); + } + // 设置请求到RequestContext中 + RequestContext::set($psr7Request); + + return [$psr7Request, $psr7Response]; +} +``` + +::: +创建一个新的 `Psr7Response` 实例,并将其设置到 `ResponseContext` 上下文中。`ResponseContext`和`RequestContext` 是静态类,用于在全局范围内存储响应对象。 +#### 路由寻址 +请求和响应对象转换完成之后,开始匹配路由, +::: code-group +```php [寻址] +$psr7Request = $this->coreMiddleware->dispatch($psr7Request); +``` +```php [路由查找] +public function dispatch(ServerRequestInterface $request): ServerRequestInterface +{ + // 进行查找路由操作 + $routes = $this->dispatcher->dispatch($request->getMethod(), $request->getUri()->getPath()); + + $dispatched = new Dispatched($routes, $this->serverName); + + return RequestContext::set($request)->setAttribute(Dispatched::class, $dispatched); +} + +``` + +```php [匹配实现] +public function dispatch($httpMethod, $uri) +{ + if (isset($this->staticRouteMap[$httpMethod][$uri])) { + $handler = $this->staticRouteMap[$httpMethod][$uri]; + return [self::FOUND, $handler, []]; + } + + $varRouteData = $this->variableRouteData; + if (isset($varRouteData[$httpMethod])) { + $result = $this->dispatchVariableRoute($varRouteData[$httpMethod], $uri); + if ($result[0] === self::FOUND) { + return $result; + } + } + + // For HEAD requests, attempt fallback to GET + if ($httpMethod === 'HEAD') { + if (isset($this->staticRouteMap['GET'][$uri])) { + $handler = $this->staticRouteMap['GET'][$uri]; + return [self::FOUND, $handler, []]; + } + if (isset($varRouteData['GET'])) { + $result = $this->dispatchVariableRoute($varRouteData['GET'], $uri); + if ($result[0] === self::FOUND) { + return $result; + } + } + } + + // If nothing else matches, try fallback routes + if (isset($this->staticRouteMap['*'][$uri])) { + $handler = $this->staticRouteMap['*'][$uri]; + return [self::FOUND, $handler, []]; + } + if (isset($varRouteData['*'])) { + $result = $this->dispatchVariableRoute($varRouteData['*'], $uri); + if ($result[0] === self::FOUND) { + return $result; + } + } + + // Find allowed methods for this URI by matching against all other HTTP methods as well + $allowedMethods = []; + + foreach ($this->staticRouteMap as $method => $uriMap) { + if ($method !== $httpMethod && isset($uriMap[$uri])) { + $allowedMethods[] = $method; + } + } + + foreach ($varRouteData as $method => $routeData) { + if ($method === $httpMethod) { + continue; + } + + $result = $this->dispatchVariableRoute($routeData, $uri); + if ($result[0] === self::FOUND) { + $allowedMethods[] = $method; + } + } + + // If there are no allowed methods the route simply does not exist + if ($allowedMethods) { + return [self::METHOD_NOT_ALLOWED, $allowedMethods]; + } + + return [self::NOT_FOUND]; +} +``` + +::: + +当匹配到时,返回参数如下所示,数组第一个参数为标识是否匹配到路由的状态码。 + +```php +Array +( + [0] => 1 + [1] => Hyperf\HttpServer\Router\Handler Object + ( + [callback] => Array + ( + [0] => App\Controller\IndexController + [1] => index + ) + + [route] => /index/index + [options] => Array + ( + [middleware] => Array + ( + ) + + ) + + ) + + [2] => Array + ( + ) + +) +``` + +```php +$dispatched = new Dispatched($routes, $this->serverName); +``` + +使用获取到的路由信息数组和当前`Server`实例化一个`Dispatched`类来保存匹配信息。 + +```php +namespace Hyperf\HttpServer\Router; + +use FastRoute\Dispatcher; + +class Dispatched +{ + public int $status; + + public ?Handler $handler = null; + + public array $params = []; + + /** + * 根据匹配信息,设置实例参数 + * + * @param array $array with one of the following formats: + * + * [Dispatcher::NOT_FOUND] + * [Dispatcher::METHOD_NOT_ALLOWED, ['GET', 'OTHER_ALLOWED_METHODS']] + * [Dispatcher::FOUND, $handler, ['varName' => 'value', ...]] + */ + public function __construct(array $array, public ?string $serverName = null) + { + $this->status = $array[0]; + switch ($this->status) { + case Dispatcher::METHOD_NOT_ALLOWED: + $this->params = $array[1]; + break; + case Dispatcher::FOUND: + $this->handler = $array[1]; + $this->params = $array[2]; + break; + } + } + + public function isFound(): bool + { + return $this->status === Dispatcher::FOUND; + } + + public function isNotFound(): bool + { + return $this->status === Dispatcher::NOT_FOUND; + } +} +``` + +最后设置请求上下文,并返回修改后的请求对象。 + +```php +return RequestContext::set($request)->setAttribute(Dispatched::class, $dispatched); +``` + +```php +$this->option?->isEnableRequestLifecycle() && $this->event?->dispatch(new RequestReceived( + request: $psr7Request, + response: $psr7Response, + server: $this->serverName +)); +``` + +如果`option`参数设置了`enable_request_lifecycle`为`true`,则触发`RequestReceived`事件。 + +```php +$dispatched = $psr7Request->getAttribute(Dispatched::class); +``` + +从请求中取出刚才设置的`Dispatched`实例。 + +#### 处理中间件 + +首先获取配置文件中的路由, +如果路由匹配成功,则获取路由上的中间件配置,然后将中间件进行合并。 +```php +$middlewares = $this->middlewares; +$registeredMiddlewares = []; +if ($dispatched->isFound()) { + $registeredMiddlewares = MiddlewareManager::get($this->serverName, $dispatched->handler->route, $psr7Request->getMethod()); + $middlewares = array_merge($middlewares, $registeredMiddlewares); +} +// 如果mustSortMiddlewares参数为true或者registeredMiddlewares中间件存在,则进行排序操作 +if ($this->option?->isMustSortMiddlewares() || $registeredMiddlewares) { + $middlewares = MiddlewareManager::sortMiddlewares($middlewares); +} +``` diff --git a/src/hyperf/路由寻址.md b/src/hyperf/路由寻址.md deleted file mode 100644 index a85ac44..0000000 --- a/src/hyperf/路由寻址.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: 路由寻址 ---- - -对请求进行匹配,放到请求的`attribute`属性中,`key`为`Dispatched::class`。 -```php -$psr7Request = $this->coreMiddleware->dispatch($psr7Request); -``` -> 文件位置:/vendor/hyperf/http-server/src/CoreMiddleware.php -```php -public function dispatch(ServerRequestInterface $request): ServerRequestInterface -{ - // 获取路由信息 - $routes = $this->dispatcher->dispatch($request->getMethod(), $request->getUri()->getPath()); - $dispatched = new Dispatched($routes, $this->serverName); - return RequestContext::set($request)->setAttribute(Dispatched::class, $dispatched); -} -``` -该行默认不触发事件,如果需要触发事件,要设置`enable_request_lifecycle`为`true`。 -```php -$this->option?->isEnableRequestLifecycle() && $this->event?->dispatch(new RequestReceived( - request: $psr7Request, - response: $psr7Response, - server: $this->serverName - )); -``` \ No newline at end of file diff --git a/src/hyperf/阅读版本.md b/src/hyperf/阅读版本.md deleted file mode 100644 index 14f4750..0000000 --- a/src/hyperf/阅读版本.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: 版本记录 ---- - -> 记录阅读代码版本 - -### docker镜像 -::: tip -基于`hyperf/hyperf:8.1-alpine-v3.18-swoole`镜像环境 -::: -### php版本 - -::: tip -`php`版本要求`>=8.1.0` -::: - -### hyperf版本 - -::: tip -`hyperf`版本`~3.1.0` -::: - -### 启动命令 -在命令行中输入以下命令启动项目 -```bash -docker run --name hyperf \ --v $PWD/skeleton:/data/project \ --w /data/project \ --p 9501:9501 -it \ ---privileged -u root \ ---entrypoint /bin/sh \ -hyperf/hyperf:8.1-alpine-v3.18-swoole -# 其中 $PWD/skeleton可替换为自己的目录 -```