makise-co / middleware
PSR-15 中间件分发器
v1.0.1
2020-10-12 11:13 UTC
Requires
- php: >=7.4
- psr/container: ^1.0
- psr/http-server-middleware: ^1.0
Requires (Dev)
- guzzlehttp/psr7: ^1.7
- phpstan/phpstan: ^0.12.48
- phpunit/phpunit: ^9.4
README
本软件包提供了PSR-15请求分发器的两种高性能实现
-
分发器 - 平坦列表实现
这是一个集中式架构,分发器充当协调者。中间件接收分发器实例作为请求处理器。分发器知道接下来需要调用哪个中间件。它的工作方式如下:
- 分发器->handle($request) ->
- Middleware1->process($request, 分发器) ->
- 分发器->handle($request) ->
- Middleware2->process($request, 分发器) ->
- 分发器->handle($request) ->
- RequestHandler->handle($request)
-
MiddlewarePipe - 链表实现(更难理解,但运行速度略快)
这是一个去中心化架构,每个管道充当请求处理器。MiddlewarePipe是中间件或请求处理器的包装器。它的工作方式如下:
- $pipeline->handle($request) ->
- Middleware1->process($request, $nextPipeline) ->
- $nextPipeline->handle($request) ->
- Middleware2->process($request, $nextPipeline) ->
- $nextPipeline->handle($request) ->
- RequestHandler->handle($request)
需求
- PHP >= 7.4
安装
composer require makise-co/middleware
基准测试
10000 次调用
Makise time took: 0.011235 secs (0.00000112 secs per request)
Makise (flat) time took: 0.011854 secs (0.00000119 secs per request)
Laminas time took: 0.034769 secs (0.00000348 secs per request)
Relay time took: 0.021777 secs (0.00000218 secs per request)
100万次调用
Makise time took: 1.077460 secs (0.00000108 secs per request)
Makise (flat) time took: 1.122735 secs (0.00000112 secs per request)
Laminas time took: 2.027957 secs (0.00000203 secs per request)
Relay time took: 1.482862 secs (0.00000148 secs per request)
- 使用的Laminas版本:3.2.2
- 使用的Relay版本:2.1.1
基准测试代码可以在这里找到。
- 基准测试在PHP 7.4上执行,并启用了OPcache。
- CPU:Intel Core i7-9750H 6核心(基准测试期间的CPU频率:4.07 GHz)
- 操作系统:Ubuntu 20.04(WSL 2)
用法
分发器(平坦列表)
<?php declare(strict_types=1); use MakiseCo\Middleware\Dispatcher; use MakiseCo\Middleware\DispatcherFactory; use MakiseCo\Middleware\MiddlewareResolver; $dispatcher = new Dispatcher([$middleware1, $middleware2, $requestHanlder]); // or you can use MiddlewareResolver with PsrContainer implementation to resolve middlewares $dispatcher = new Dispatcher( [Middleware1::class, Middleware2::class, RequestHandler::class], new MiddlewareResolver($container) ); // or you can use Dispatcher factory (with optional MiddlewareResolver) $factory = new DispatcherFactory(new MiddlewareResolver($container)); $dispatcher = $factory->create([Middleware1::class, Middleware2::class, RequestHandler::class]); $response = $dispatcher->handle($request);
MiddlewarePipe(链表)
创建中间件管道
<?php declare(strict_types=1); use MakiseCo\Middleware\MiddlewarePipeFactory; use MakiseCo\Middleware\MiddlewareResolver; $factory = new MiddlewarePipeFactory(); $pipeline = $factory->create([$middleware1, $middleware2, $requestHanlder]); // or you can use MiddlewareResolver with PsrContainer implementation to resolve middlewares $factory = new MiddlewarePipeFactory(new MiddlewareResolver($container)); $pipeline = $factory->create([Middleware1::class, MIddleware2::class, RequestHandler::class]); $response = $pipeline->handle($request);
每个管道必须以响应生产者结束,否则管道将因RuntimeException (Empty handler)失败
合并管道
<?php declare(strict_types=1); use MakiseCo\Middleware\MiddlewarePipeFactory; $factory = new MiddlewarePipeFactory(); $subPipeline = $factory->create([$middleware1_1, $middleware1_2]); $pipeline = $factory->create([$middleware1, $subPipeline, $middleware2, $requestHanlder]);
执行顺序将是:middleware1 -> middleware1_1 -> middleware1_2 -> middleware2 -> requestHandler
添加错误处理中间件
根据PSR-15,错误处理中间件必须是管道中的第一个中间件。
<?php declare(strict_types=1); use MakiseCo\Middleware\ErrorHandlingMiddleware; use MakiseCo\Middleware\MiddlewarePipeFactory; use Psr\Log\LoggerInterface; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; class HttpErrorHandler implements \MakiseCo\Middleware\ErrorHandlerInterface { private ResponseFactoryInterface $responseFactory; private LoggerInterface $logger; private bool $debug; public function __construct(ResponseFactoryInterface $responseFactory, LoggerInterface $logger, bool $debug) { $this->responseFactory = $responseFactory; $this->logger = $logger; $this->debug = $debug; } public function handle(Throwable $e, ServerRequestInterface $request): ResponseInterface { // TODO: Write own error handler $this->logger->error(...); if ($this->debug) { // TODO: Create detailed error response } return $this->responseFactory->createResponse(500); } } $errorHandlingMiddleware = new ErrorHandlingMiddleware( new HttpErrorHandler($responseFactory, $logger, true) ); $factory = new MiddlewarePipeFactory(); $pipeline = $factory->create([ $errorHandlingMiddleware, $middleware1, $middleware2, $requestHandler, // ... ]); $response = $pipeline->handle($request);