makise-co/middleware

PSR-15 中间件分发器

v1.0.1 2020-10-12 11:13 UTC

This package is auto-updated.

Last update: 2024-09-12 20:11:52 UTC


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);