yoozoo/yii2-psr7-bridge

Yii2 的 PSR7 桥接器

0.0.2 2019-06-18 03:58 UTC

This package is auto-updated.

Last update: 2024-09-25 14:07:13 UTC


README

Yii2 网络应用的 PSR-7 桥接器和 PSR-15 适配器。

该桥接器的使用场景是使 Yii2 能够与 PSR-7 和 PSR-15 中间件和任务运行器(如 RoadRunner 和 PHP-PM)一起使用,而无需对应用程序进行(例如,不需要更改应用程序中对 Yii::$app->requestYii::$app->response 的任何调用)最小代码更改。

请注意,目前该桥接器的质量非常初级。它“工作”是因为可以接受 PSR7 请求作为输入,并返回一个有效且大部分符合预期的 PSR7 响应。

然而,一些特性和功能尚缺失。许多功能无法正常工作,其他功能有不可预期的副作用。建议您目前不要使用此软件包。

请参阅 README 文件底部的 当前状态 清单,了解已实现的功能以及我们可以帮助解决的问题。

需要帮助什么?

欢迎贡献者!请查看 当前状态 清单,了解还需要实现的功能,帮助添加测试或添加新功能!

安装

此软件包可以通过 Composer 安装

composer require charlesportwoodii/yii2-psr7-bridge

测试

可以使用 phpunit 运行测试。

./vendor/bin/phpunit

用法

由于此软件包的特性,需要对您的应用程序进行一些更改。

分发器

  1. 修改您的网络应用程序配置中的 requestresponse 组件,分别设置为 yii\Psr7\web\Requestyii\Psr7\web\Response 的实例
return [
    // Other flags
    'components' => [
        'request' => [
            'class' => \yii\Psr7\web\Request::class,
        ],
        'response' => [
            'class' => \yii\Psr7\web\Response::class
        ],
        // Other components
    ]
];

如果您正在使用自定义 Request 类,只需简单地让它在 yii\Psr7\web\Request 上进行重载以继承基本功能。

  1. 将以下环境变量设置到您的任务运行器中。例如,对于 RoadRunner,您的配置可能如下所示
env:
  YII_ALIAS_WEBROOT: /path/to/webroot
  YII_ALIAS_WEB: '127.0.0.1:8080'

必须定义所有环境变量。

  1. 使用 PSR-15 兼容的分发器运行您的应用程序。

例如,对于 Go/Roadrunner,您可以使用以下组件

#!/usr/bin/env php
<?php

ini_set('display_errors', 'stderr');

// Set your normal YII_ definitions
defined('YII_DEBUG') or define('YII_DEBUG', true);
// Alternatives set this in your rr.yaml file
//defined('YII_DEBUG') or define('YII_DEBUG', \getenv('YII_DEBUG'));

defined('YII_ENV') or define('YII_ENV', 'dev');
// Alternatives set this in your rr.yaml file
//defined('YII_ENV') or define('YII_ENV', \getenv('YII_ENV'));

// Load your vendor dependencies and Yii2 autoloader
require_once '/path/to/vendor/autoload.php';
require_once '/path/to/vendor/yiisoft/yii2/Yii.php';

// Roadrunner relay and PSR7 object
$relay = new \Spiral\Goridge\StreamRelay(STDIN, STDOUT);
$psr7 = new \Spiral\RoadRunner\PSR7Client(new \Spiral\RoadRunner\Worker($relay));

// Load your configuration file
$config = require_once '/path/to/config/config.php';

$application = (new \yii\Psr7\web\Application($config));

// Handle each request in a loop
while ($request = $psr7->acceptRequest()) {
    try {
        $response = $application->handle($request);
        $psr7->respond($response);
    } catch (\Throwable $e) {
        // \yii\Psr7\web\ErrorHandler should handle any exceptions
        // however you should implement your custom error handler should anything slip past.
        $psr7->getWorker()->error((string)$e);
    }

    // Workers will steadily grow in memory with each request until PHP memory_limit is reached, resulting in a worker crash.
    // With RoadRunner, you can tell the worker to shutdown if it approaches 10% of the maximum memory limit, allowing you to achieve better uptime.
    if ($application->clean()) {
        $psr7->getWorker()->stop();
        return;
    }
}

工作进程崩溃保护

随着每个请求的提交,PHP 的内存使用量会逐渐增加。调用 $application->clean() 将告诉您当前脚本的内存使用是否在 memory_limit ini 设置的 10% 以内。虽然大多数工作进程将处理内存不足的崩溃异常,但您可以使用此方法显式地告诉当前工作进程停止并重建,以避免由于内存不足问题而抛出的 HTTP 500 错误。

会话

此库完全兼容 yii\web\Session 和从其派生的类,但有一些注意事项。

  1. 应用程序组件在运行时添加以下会话 ini 设置。不要覆盖这些设置,因为它们对于 yii\web\Session 是必要的。
ini_set('use_cookies', 'false');
ini_set('use_only_cookies', 'true');
  1. 不要在您的任务运行器中访问 $application->getSession()

请求

yii\Psr7\web\Request 定义了一个用于替代 yii\web\Request 的占位符。要访问原始 PSR-7 对象,请调用 Yii::$app->request->getPsr7Request()

响应

yii\Psr7\web\Response 直接扩展了 yii\web\Response。所有功能都由 yii\Psr7\web\traits\Psr7ResponseTrait 实现,您可以在任何自定义响应类中作为 trait 使用它。或者,您可以直接扩展 yii\Psr7\web\Response

错误处理器

yii\Psr7\web\ErrorHandler 实现了与 yii\web\ErrorHandler 兼容的自定义错误处理。 yii\Psr7\web\Application 自动使用此错误处理器。如果您有自定义错误处理器,请将其扩展为 yii\Psr7\web\ErrorHandler

通过 errorAction 支持正常功能。Yii2 的标准错误和异常页面应该能够直接使用。

PSR-7 和 PSR-15 兼容性

\yii\Psr7\web\Application 扩展 \yii\web\Application 并实现了 PSR-15 的 \Psr\Http\Server\RequestHandlerInterface 接口,提供了完整的 PSR-15 兼容性。

PSR-7

如果你的应用程序不需要 PSR-15 中间件,你可以简单地从应用程序中返回一个 PSR-7 响应,如下所示

$response = $application->handle($request);
$psr7->respond($response);

这种配置中不需要调度器。

带有中间件/工具包的 PSR-15

由于 \yii\Psr7\web\Application 与 PSR-15 中间件兼容,你还可以使用它与任何 PSR-15 调度器一起使用。

这个库没有实现自己的调度器,允许开发者自由选择用于中间件处理的 PSR-15 兼容调度器。

以下是一个使用 middlewares/utils 的示例

$response = \Middlewares\Utils\Dispatcher::run([
    // new Middleware,
    // new NextMiddleware, // and so forth...
    function($request, $next) use ($application) {
        return $application->handle($request);
    }
], $request);

// rr response
$psr7->respond($response);

PSR-15 中间件过滤器

此包还提供了通过 yii\base\ActionFilter 扩展按路由处理 PSR-15 兼容中间件的能力。

通过这些方法运行的中间件不是 100% 遵循 PSR-15,因为它们在自己的沙箱中执行,独立于任何调度器中声明的中间件。这些中间件仅在动作过滤器本身的上下文中操作。例如,middlewares/request-time 中间件将只测量动作过滤器的运行时间,而不是整个请求。如果您需要这些中间件在更高的链中工作,请在您的主调度器中将它们链接起来,或者考虑使用原生的 Yii2 ActionFilter。

身份验证

如果您的应用程序需要由现有 yii\filters\auth\AuthInterface不提供 的 PSR-15 身份验证中间件,您可以使用 yii\Psr7\filters\auth\MiddlewareAuth 来处理您的 PSR-15 身份验证中间件。

\yii\Psr7\filters\auth\MiddlewareAuth 将运行身份验证中间件。如果您的中间件返回了一个响应,它将发送该响应。否则,它将查找您的身份验证中间件指定的 attribute,并使用存储在该属性中的值运行 yii\web\User::loginByAccessToken()

注意您的 yii\web\UserIdentityInterface 应该配置为处理您提供的请求属性。由于大多数身份验证中间件导出包含用户信息的属性,因此应使用该属性与 Yii2 的 IdentityInterface 进行接口交互。

以下是一个使用 middlewares/http-authentication 的简单示例,使用由 Middlewares\BasicAuthentication 填充的 username 属性。

public function behaviors()
{
    return \array_merge(parent::behaviors(), [
        [
            'class' => \yii\Psr7\filters\auth\MiddlewareAuth::class,
            'attribute' => 'username',
            'middleware' => (new \Middlewares\BasicAuthentication(
                Yii::$app->user->getUsers() // Assumes your `IdentityInterface` class has a method call `getUsers()` that returns an array of username/password pairs
                /**
                 * Alternatively, just a simple array for you to map back to Yii2
                 *
                 * [
                 *     'username1' => 'password1',
                 *     'username2' => 'password2'
                 * ]
                 */
            ))->attribute('username')
        ]
    ];
}

注意:此类应与 yii\filters\auth\CompositeAuth 兼容,因为它返回 null。但是,如果返回了 HTTP 状态代码或消息,则 yii\web\Response 对象 被填充。如果您需要自定义响应,应扩展此类或手动触发 handleFailure()

其他中间件

yii\Psr7\filters\MiddlewareActionFilter 可以用于处理其他 PSR-15 兼容中间件。每个中间件将按顺序执行,并返回该中间件的有效响应。

例如:使用 middlewares/client-ipmiddlewares/uuid 返回请求的响应时间和一个 UUID。

public function behaviors()
{
    return \array_merge(parent::behaviors(), [
        [
            'class' => \yii\Psr7\filters\MiddlewareActionFilter::class,
            'middlewares' => [
                // Yii::$app->request->getAttribute('client-ip') will return the client IP
                new \Middlewares\ClientIp,
                // Yii::$app->response->headers['X-Uuid'] will be set
                new \Middlewares\Uuid,
            ]
        ]
    ];
}

中间件处理还支持 PSR-15 兼容的闭包。

public function behaviors()
{
    return \array_merge(parent::behaviors(), [
        [
            'class' => \yii\Psr7\filters\MiddlewareActionFilter::class,
            'middlewares' => [
                function ($request, $next) {
                    // Yii::$app->request->getAttribute('foo') will be set to `bar`
                    // Yii::$app->response->headers['hello'] will be set to `world`
                    return $next->handle(
                        $request->withAttribute('foo', 'bar')
                    )->withHeader('hello', 'world')
                }
            ]
        ]
    ];
}

中间件将按顺序处理,直到返回一个响应(如 HTTP 重定向)或所有中间件都已处理。

如果有中间件执行并返回响应,则动作过滤器的 before 方法将返回 false,并将生成的响应发送给客户端。

任何之前中间件添加的请求属性或头都会包含在响应中。

为什么这个包存在?

性能

任务运行器(如 RoadRunner 和 PHP-PM)的性能优势非常明显,难以忽视。

尽管PHP从7.0(phpng)开始逐渐提升了速度,但基于Web的PHP应用程序的性能仍然受到每次HTTP请求都需要重新引导每个文件的限制。尽管Nginx + PHP-FPM运行速度快,即使使用opcache,每个文件也必须在每次HTTP请求中重新读入内存。

PSR-7服务器使我们能够在请求之间保持几乎所有类和代码在内存中,这基本上消除了最大的性能瓶颈。

请务必查看性能比较维基页面,了解yii2-app-basic应用程序的实际性能影响。

预计PHP 7.4预加载将进一步提高性能。

PSR-7和PSR-15兼容性

虽然本项目并非严格的目标,但越来越难以忽视PSR-7和PSR-15中间件。由于Yii2团队将PSR-7兼容性推迟到了Yii 2.1或Yii 3,现有的Yii2项目无法利用标准化的请求/响应模式或链式中间件。

遵守PSR-7和PSR-15的开发者需要重新实现Yii2的定制中间件,这与Yii2的快速、安全、高效的口号相悖。这个库有助于减轻一些痛苦。

这是如何工作的

该软件包在yii\Psr7\web命名空间中提供了三个类:ApplicationRequestResponse,以及一个Psr7ResponseTrait特性,可以用于将PSR7响应能力添加到现有的扩展yii\web\Response的类中。

为了处理传入的请求,yii\Psr7\web\Application组件作为yii\web\Application的替代品,用于在您的任务运行程序中使用。它的构造函数接受标准的Yii2配置数组和一个额外的ServerRequestInterface实例。然后,Application组件使用提供的ServerRequestInterface实例实例化一个yii\Psr7\web\Request对象。

由于yii\web\Application::bootstrap使用request组件,因此需要在应用程序构造函数期间正确构建请求组件,而不是简单地调用$app->handleRequest($psr7Request);

yii\Psr7\web\Requestyii\web\Request的替代品。它的唯一目的是在ServerRequestInterface和标准的yii\web\Request API之间提供一个接口。

在您的模块、控制器、动作中,可以像平常一样使用Yii::$app->requestYii::$app->response,无需任何更改。

在应用程序存在之前,它将在您的响应组件上调用getPsr7Response。如果您使用的是yii\web\Response,只需将应用程序配置中的响应组件类更改为yii\Psr7\web\Response。如果您使用的是自定义的Response对象,只需将yii\Psr7\web\traits\Psr7ResponseTrait特性添加到扩展yii\web\ResponseResponse对象中,即可获得必要的行为。

限制

  • 当前文件流不工作(例如yii\web\Response::sendFileyii\web\Response::sendContentAsFileyii\web\Response::sendStreamAsFile)。
  • Yii2调试工具栏yii2-debug将显示错误请求时间和内存使用情况。
  • Yii2不能发送SameSite cookie。

当前状态

  • 实现自定义`Application`组件。
  • 将PSR7请求转换为yii\web\Request对象。
  • 返回简单的响应。
  • 路由。
  • 处理yii\web\Response::$format
  • 与标准Yii2格式器一起工作。
  • 处理HeaderCollection
  • 处理CookieCollection
  • 处理yii\web\Response::$streamyii\web\Response::$content
  • 实现yii\web\Response::redirect
  • 实现yii\web\Response::refresh
  • 获取GET查询参数 yii\web\Request::get()
  • 获取POST参数 yii\web\Request::post()
  • yii\web\Request::getAuthCredentials().
  • yii\web\Request::loadCookies().
  • 按动作中间件认证处理。
  • 按动作中间件链。
  • 重用Application组件,而不是在每个循环中重新实例化。
  • yii\web\ErrorHandler实现。
  • 运行yii-app-basic
  • 使用yii\log\Target引导。
  • 会话处理
  • yii-debug.
  • yii-gii.
  • 解决负载下的致命内存泄漏
  • yii\filters\auth\CompositeAuth兼容性。
  • 实现可比较的sendFile
  • 支持yii\web\Request::$methodParam。 (实际上不适用于ServerRequestInterface)
  • 测试覆盖率

本项目采用BSD-3-Clause许可。有关更多详细信息,请参阅LICENSE