itantik/middleware

通用 PHP 中间件实现

v0.2.1 2020-05-27 18:23 UTC

This package is auto-updated.

Last update: 2024-09-28 04:53:55 UTC


README

通用 PHP 中间件实现。虽然它与任何 PSR 都有相似之处,但并不符合 PSR 规范。主要目标是提供一个通用的中间件处理器,而不仅仅是 HTTP 请求/响应处理器。

安装

composer require itantik/middleware

用法

让我们看一个例子。

请求

请求实现 Itantik\Middleware\IRequest 接口。否则它是一个普通对象,通常是数据传输对象。

class LoggableRequest implements IRequest
{
    /** @var string[] */
    private $logs = [];

    public function addLog(string $message): void
    {
        $this->logs[] = $message;
    }

    /**
     * @return string[]
     */
    public function getLogs(): array
    {
        return $this->logs;
    }
}

响应

与请求类似,响应也是一个实现 Itantik\Middleware\IResponse 接口的普通对象。

class LoggableResponse implements IResponse
{
    /** @var string[] */
    private $logs = [];

    public function addLog(string $message): void
    {
        $this->logs[] = $message;
    }

    /**
     * @return string[]
     */
    public function getLogs(): array
    {
        return $this->logs;
    }
}

中间件

中间件实现 Itantik\Middleware\IMiddleware 接口。

class FirstMiddleware implements IMiddleware
{
    public function handle(IRequest $request, ILayer $nextLayer): IResponse
    {
        // do something BEFORE next middleware
        if ($request instanceof LoggableRequest) {
            $request->addLog('FirstMiddleware begin');
        }

        // MUST invoke next middleware
        $resp = $nextLayer->handle($request);

        // do something AFTER next middleware
        if ($resp instanceof LoggableResponse) {
            $resp->addLog('FirstMiddleware end');
        }

        // MUST return response
        return $resp;
    }
}

class SecondMiddleware implements IMiddleware
{
    public function handle(IRequest $request, ILayer $nextLayer): IResponse
    {
        // here do nothing BEFORE next middleware

        // MUST invoke next middleware
        $resp = $nextLayer->handle($request);

        // do something AFTER next middleware
        if ($resp instanceof LoggableResponse) {
            $resp->addLog('SecondMiddleware end');
        }

        // MUST return response
        return $resp;
    }
}

一个示例中间件,它将每个请求处理器包装在一个数据库事务中。

class TransactionalMiddleware implements IMiddleware
{
    /** @var Connection */
    private $connection;

    public function __construct(Connection $connection)
    {
        $this->connection = $connection;
    }

    public function handle(IRequest $request, ILayer $nextLayer): IResponse
    {
        $connection = $this->connection;

        // begin database transaction
        $connection->beginTransaction();

        try {
            // invoke next middleware
            $resp = $nextLayer->handle($request);

            // commit database transaction
            $connection->commit();
        } catch (Exception $e) {
            // rollback database transaction
            $connection->rollback();
            throw $e;
        }

        // return response
        return $resp;
    }
}

核心层

核心层是中间件链中的最后一个部分。它处理请求并返回响应。核心层实现 Itantik\Middleware\ILayer 接口。

class CoreLayer implements ILayer
{
    public function handle(IRequest $request): IResponse
    {
        if ($request instanceof LoggableRequest) {
            $request->addLog('CoreLayer begin');
        }

        // create response
        $resp = new LoggableResponse();
        $resp->addLog('CoreLayer end');

        // return response
        return $resp;
    }
}

中间件管理器

中间件管理器注册所有中间件,通过它们处理给定的请求并返回响应。

// create middleware manager
$manager = new \Itantik\Middleware\Manager();

// register middlewares
$manager->append(new FirstMiddleware());
$manager->append(new SecondMiddleware());

// create request
$request = new LoggableRequest();

// create core layer for this request
$coreLayer = new CoreLayer();

// run it
$response = $manager->process($request, $coreLayer);

// expected result
$requestLogs = $request->getLogs();
// [
//    'FirstMiddleware begin',
//    'CoreLayer begin',
// ]
$responseLogs = $response->getLogs();
// [
//    'CoreLayer end',
//    'SecondMiddleware end',
//    'FirstMiddleware end',
// ]

// now we can take another request with appropriate core layer that handles it
// and run middleware manager again
$response = $manager->process($anotherRequest, $anotherLayer);
// and again

传输层

传输层是中间件链中的一个部分。它持有中间件实例和下一层实例。在 handle 方法中,它使用请求和下一层调用中间件处理器。

管理器使用默认的传输层,但你可以创建自己的传输层并向其添加额外的功能。例如,如果你使用来自不受信任来源的中间件,你可以执行一些检查。

// transport layer object
class DataTransportLayer implements ILayer
{
    /** @var IMiddleware */
    private $middleware;
    /** @var ILayer */
    private $nextLayer;

    public function __construct(IMiddleware $middleware, ILayer $nextLayer)
    {
        $this->middleware = $middleware;
        $this->nextLayer = $nextLayer;
    }

    /**
     * @param IRequest $request
     * @return IResponse
     * @throws MiddlewareException
     */
    public function handle(IRequest $request): IResponse
    {
        $res = $this->middleware->handle($request, $this->nextLayer);

        // check a correct response type returned from each middleware handler
        if (!($res instanceof DataResponse)) {
            throw new MiddlewareException(
                sprintf("Middleware handler must return an instance of '%s'.", DataResponse::class)
            );
        }
        return $res;
    }
}

// transport layer factory
class DataTransportLayerFactory implements ITransportLayerFactory
{
    public function create(IMiddleware $middleware, ILayer $nextLayer): ILayer
    {
        return new DataTransportLayer($middleware, $nextLayer);
    }
}

// add transport layer factory to the manager
$manager = new \Itantik\Middleware\Manager(new DataTransportLayerFactory());

要求

  • PHP 7.2