jschreuder/middle

基于其他组件构建的中间件微框架

2.0.0 2023-02-05 21:36 UTC

This package is auto-updated.

Last update: 2024-09-06 01:06:55 UTC


README

Scrutinizer Code Quality Scrutinizer Build Status Scrutinizer Build Status

一个围绕中间件概念的微框架,基本上:中间件一切。这意味着什么?一切都是基于简单的接口,默认实现可以被替换或装饰。实现可以是原子的:只执行一个任务。通过选择要装饰或添加到堆栈中的简单中间件来组合复杂的功能。此外,每个组件都是 NIH;PSR-1、PSR-2、PSR-3、PSR-4、PSR-7、PSR-15 和 PSR-17;自 2.0 版本起针对 PHP 8.1。

查看 Middle 骨架 应用程序以快速运行示例设置。

注意:所有示例都使用 Laminas Diactoros,但任何 PSR-7 兼容库也可以正常工作。

运行 Middle 应用程序

使用 Middle 构建的应用程序的核心是 ApplicationStackInterface,它仅接受 PSR ServerRequestInterface 并必须返回一个 ResponseInterface。运行它,在设置完成后,如下所示(使用 Laminas Diactoros 作为 PSR-7 实现)

<?php
// Create Request
$request = Laminas\Diactoros\ServerRequestFactory::fromGlobals();

// Render the response by processing the request
$response = $app->process($request);

// And output it
(new Laminas\Diactoros\Response\SapiEmitter())->emit($response);

最小默认设置

以下设置应用程序,以路由中间件和控制器运行器为核心。中间件以 LIFO(后进先出)顺序处理。

<?php
use jschreuder\Middle;
// Let's setup a router which needs the base-URL, and a handler for
// generating a response when no route is matched.
$router = new Middle\Router\SymfonyRouter('https://');
$fallbackController = Middle\Controller\CallableController::fromCallable(
    function () {
        return new Laminas\Diactoros\Response\JsonResponse(['error'], 400);
    }
);

// Setup the application with controller runner and the routing middleware
$app = new Middle\ApplicationStack(
    new Middle\Controller\ControllerRunner(),
    new Middle\ServerMiddleware\RoutingMiddleware($router, $fallbackController)
);

有了这个设置,我们现在可以添加一些路由(使用上面的 $router

<?php
use jschreuder\Middle;
// Using the convenience method for GET request on '/'
$router->get('home', '/',
    Middle\Controller\CallableController::factoryFromCallable(function () {
        return new Laminas\Diactoros\Response\JsonResponse([
            'message' => 'Welcome to our homepage',
        ]);
    })
);

包含的路由依赖于 Symfony 的路由组件。在路径中,您可以使用变量表示法。get() 方法还支持 2 个额外的参数:传递给 Symfony 路由的 $defaults 数组和 $requirements 数组。

向堆栈添加更多中间件

以下示例构建了一个应用程序,它使用 2 个额外的中间件,在先前的示例之上添加了会话和错误处理。

<?php
use jschreuder\Middle;
// starting with the example above, let's add these before running the app.

// Now let's also make sessions available on the request
$app = $app->withMiddleware(
    new Middle\ServerMiddleware\SessionMiddleware(
        new Middle\Session\LaminasSessionProcessor()
    )
);

// And finally: make sure any errors are caught
$app = $app->withMiddleware(
    new Middle\ServerMiddleware\ErrorHandlerMiddleware(
        new Monolog\Logger(...),
        function (Psr\Http\Message\ServerRequestInterface $request, \Throwable $exception) {
            return new Laminas\Diactoros\Response\JsonResponse(['error'], 500);
        }
    )
);

会话中间件在 ServerRequest 的属性中添加一个 'session' 属性,其中包含 jschreuder\Middle\Session\SessionInterface 的实例。

错误处理器接受一个 PSR-3 LoggerInterface 实例,它将记录任何未捕获的异常作为 alert。构造函数中的可调用对象将在之后直接调用,并期望返回一个显示错误给用户的 ResponseInterface

也支持模板

还有一个内置的通用模板解决方案。要使用它,控制器可以创建一个中间的 ViewInterface 实例,并接受一个 RendererInterface 实例,将其渲染到响应对象中。

以下示例使用内置的 Twig 渲染器

<?php
use jschreuder\Middle;
// Setup the renderer for Twig with a Twig_Environment instance and a
// PSR-17 Response factory for generating the Response object
$renderer = new Middle\View\TwigRenderer(
    new Twig\Environment(...),
    $responseFactory
);

$router->get('home', '/',
    Middle\Controller\CallableViewController::factoryFromCallable(
        function (Psr\Http\Message\ServerRequestInterface $request) use ($renderer) {
            // Should render template.twig and parameters with Twig and return
            // response with status code 200
            return $renderer->render($request, new Middle\View\View('template.twig', [
                'view' => 'parameters',
            ], 200));
        }
    );
);

RendererInterface 可以被装饰。如果您想使用视图返回重定向,您可以像这样装饰渲染器

<?php
use jschreuder\Middle;
// Decorate with the RedirectRendererMiddleware which needs a PSR-17
// Response factory for generating the Response object
$renderer = new Middle\View\RedirectRendererMiddleware(
    $renderer,
    $responseFactory
);

完成之后,您可以创建这样的重定向

<?php
use jschreuder\Middle;
$router->get('redirect.example', '/redirect/to/home',
    Middle\Controller\CallableViewController::factoryFromCallable(
        function (Psr\Http\Message\ServerRequestInterface $request) use ($renderer) {
            // This will redirect to the path '/' with status 302, the status is
            // optional and will default to 302 when omitted.
            return $renderer->render($request, new Middle\View\RedirectView('/', 302));
        }
    );
);

中间件和依赖注入容器

以下示例使用 Pimple,但可能在其他容器中也可以使用相同的概念

<?php
use jschreuder\Middle;
// First create the central app object in the container
$container = Pimple\Container();
$container['app'] = function () {
    return new Middle\ApplicationStack(
        new Middle\Controller\ControllerRunner()
    );
};

// Now to add a middleware you can do this
$container->extend('app',
    function (Middle\ApplicationStack $app, Pimple\Container $container) {
        return $app->withMiddleware(
            new Middle\ServerMiddleware\RoutingMiddleware(
                $container['router'], $container['fallbackHandler']
            )
        );
    }
);

当在多个地方这样做时,例如通过服务提供者,顺序可能不那么明确,因此请注意添加中间件的顺序。

包含的服务

有一些包含的服务,它们都有自己的默认实现,可以根据需要替换或装饰

  • SessionProcessorInterface 与其默认选项取决于 laminas/laminas-session。它允许设置和获取值、销毁会话或轮换其 ID。如上所示,可以通过 SessionMiddleware 加载 LaminasSessionProcessor
  • RouterInterface 与 Symfony 路由组件的默认设置相关。它通过上面的 RoutingMiddleware 加载。它有添加常用 HTTP 方法、解析请求以及获取 URL 生成器以便反向路由的方法。相关接口包括 RouteMatchInterfaceUrlGeneratorInterfaceRoutingProviderInterface
  • RendererInterface 与其默认设置相关,依赖于 Twig 来渲染模板,如上面所示。您也可以将其包装在其他 Middlewares 中进行额外的解析或完全替换。相关的 ViewInterface 预期会被提供,并且包含渲染模板所需的信息。

问题及答案

  1. 为什么又是一个微型框架... 我曾使用 Silex 创建应用程序,但它却给我带来了麻烦。我还更倾向于使用 PSR-7 而不是 Symfony 的实现。我开始重构它,并用其路由组件、Twig 以及 Laminas 的 Diactoros 和 Session 库来替换它。过了一段时间后,我意识到我实际上创建了一个微型框架,并将其从我的应用程序中提取出来。
  2. 为什么所有类都是最终的? 目的是遵循 SOLID 原则中的 开闭原则。这意味着对扩展开放,对修改封闭。每个依赖都通过接口类型提示,而不是针对任何具体实现。所有类都可以通过中间件进行扩展,无论是像 ApplicationStack 那样,还是通过使用 装饰器模式。因此,您可以扩展或替换任何类,但不能修改它们的内部工作方式。因此,只有接口是框架 API 的组成部分。
  3. 我必须使用 Twig、Symfony 的路由或 Laminas 的 Session 库吗? 不,但只包含了一些基本组件。提供的是使用这些包实现的。您可以通过使用其他库实现 Routing 或 Session 接口来轻松替换它们。