charlesportwoodii / yii2-psr7-bridge
为 Yii2 服务的 PSR7 桥接器
Requires
- laminas/laminas-diactoros: ^2.3
- psr/http-message: ^1.0
- psr/http-server-handler: ^1.0
- yidas/yii2-bower-asset: 2.0.13.1
- yiisoft/yii2: ^2.0.15
Requires (Dev)
- monolog/monolog: ~2.1.0
- phpunit/phpunit: ^9.0
- samdark/yii2-psr-log-target: ^1.0
- spiral/roadrunner: ^2.5
- squizlabs/php_codesniffer: ^3.2
This package is auto-updated.
Last update: 2024-09-18 00:25:58 UTC
README
为 Yii2 网络应用提供 PSR-7 桥接器和 PSR-15 适配器。
此桥接器的用例是使 Yii2 能够使用 PSR-7 和 PSR-15 中间件和任务运行器,例如 RoadRunner 和 PHP-PM,而无需对您的应用程序进行(例如,无需修改应用程序中任何对 Yii::$app->request
和 Yii::$app->response
的调用)最小代码更改。
请注意,这目前处于非常原始的 alpha 质量。它“工作”是因为接受 PSR7 请求作为输入,并返回一个有效的 PSR7 响应,该响应通常与您所期望的相符。
但是,缺少功能和功能。许多事情不起作用,其他事情有意外的影响。建议您在此阶段不要使用此软件包。
请参阅 README 文件底部的
当前状态
清单,了解当前已实施的内容以及我们可以使用的帮助。
需要帮助什么?
欢迎贡献者!查看 当前状态
清单,了解仍需实施的事项,帮助添加测试或添加新功能!
安装
此软件包可以通过 Composer 安装。
composer require charlesportwoodii/yii2-psr7-bridge
测试
可以使用 phpunit
运行测试。
./vendor/bin/phpunit
使用方法
由于此软件包的性质,需要对您的应用程序进行一些更改。
分发器
- 修改您的网络应用程序配置中的
request
和response
组件,使其分别成为yii\Psr7\web\Request
和yii\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
以继承基本功能即可。
- 将以下环境变量设置到您的任务运行器。对于 RoadRunner,您的配置可能如下所示
env: YII_ALIAS_WEBROOT: /path/to/webroot YII_ALIAS_WEB: '127.0.0.1:8080'
必须定义所有环境变量。
- 使用与 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')); require __DIR__ . '/../vendor/autoload.php'; require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php'; $worker = Spiral\RoadRunner\Worker::create(); $psrServerFactory = new Laminas\Diactoros\ServerRequestFactory(); $psrStreamFactory = new Laminas\Diactoros\StreamFactory(); $psrUploadFileFactory = new Laminas\Diactoros\UploadedFileFactory(); $psr7 = new Spiral\RoadRunner\Http\PSR7Worker($worker, $psrServerFactory, $psrStreamFactory, $psrUploadFileFactory); $config = require __DIR__ . '/../config/web.php'; $application = (new \yii\Psr7\web\Application($config)); // Handle each request in a loop try { while ($request = $psr7->waitRequest()) { if (($request instanceof Psr\Http\Message\ServerRequestInterface)) { try { $response = $application->handle($request); $psr7->respond($response); } catch (\Throwable $e) { $psr7->getWorker()->error((string)$e); } if ($application->clean()) { $psr7->getWorker()->stop(); return; } } } } catch (\Throwable $e) { $psr7->getWorker()->error((string)$e); }
工作进程崩溃保护
由于每个请求 PHP 的内存使用量都会逐渐增加。这是不可避免的,因为 Yii2 不是为正常的 PHP 调用堆栈(Nginx + PHP-FPM、Apache2 CGI 等)设计的,而是为请求-请求工具(如 RoadRunner)设计的。调用 $application->clean()
将告诉您当前脚本使用是否在 memory_limit
ini 设置的 10% 以内。虽然大多数工作进程将处理内存不足崩溃异常,但您可以使用此方法明确告诉当前工作进程停止并重建,以避免由内存不足问题引发的 HTTP 500 错误。这将解决由于框架内存预留而产生的任何意外内存泄漏。
会话
此库完全兼容 yii\web\Session
和派生自它的类,但有几点注意事项。
- 应用程序组件在运行时添加以下 session ini 设置。不要覆盖这些设置,因为它们对于
yii\web\Session
是必要的。
ini_set('use_cookies', 'false'); ini_set('use_only_cookies', 'true');
- 不要在您的任务进程中访问
$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
实现,您可以在任何自定义响应类中作为特性使用它。或者,您也可以直接扩展 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。
身份验证
如果您的应用程序需要 PSR-15 身份验证中间件(非现有 yii\filters\auth\AuthInterface
类提供的),您可以使用 yii\Psr7\filters\auth\MiddlewareAuth
来处理您的 PSR-15 身份验证中间件。
\yii\Psr7\filters\auth\MiddlewareAuth
将运行身份验证中间件。如果您的中间件返回了一个响应,它将发送那个响应。否则,它将查找您的身份验证中间件指定的 attribute
,并使用存储在该属性中的值运行 yii\web\User::loginByAccessToken()
。
注意您的
yii\web\User
和IdentityInterface
应配置为处理您提供的请求属性。由于大多数身份验证中间件导出一个包含用户信息的属性,因此应使用它来与 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-ip
和 middlewares/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 action过滤器将返回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
命名空间内提供了三个类,分别是Application
、Request
和Response
,以及一个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\Request
是标准yii\web\Request
的替代品。它的唯一目的是在ServerRequestInterface
和标准yii\web\Request
API之间提供一个接口。
在您的模块、控制器、操作中,可以正常使用Yii::$app->request
和Yii::$app->response
,无需任何更改。
在应用程序存在之前,它将对您的响应组件调用getPsr7Response
。如果您使用的是yii\web\Response
,只需将您的应用程序配置中的响应组件类更改为yii\Psr7\web\Response
。如果您使用的是自定义的Response
对象,只需将yii\Psr7\web\traits\Psr7ResponseTrait
特性添加到扩展yii\web\Response
的Response
对象中,以获得必要的行为。
限制
- 文件流当前不起作用(例如
yii\web\Response::sendFile
、yii\web\Response::sendContentAsFile
、yii\web\Response::sendStreamAsFile
)。 - Yii2调试工具栏
yii2-debug
将显示错误的请求时间和内存使用。 - Yii2无法发送
SameSite
cookie。
当前状态
- 实现自定义`Application`组件。
- 将PSR7请求转换为
yii\web\Request
对象。 - 返回简单响应。
- 路由。
- 处理
yii\web\Response::$format
。 - 与标准Yii2格式化程序一起工作。
- 处理
HeaderCollection
。 - 处理
CookieCollection
。 - 处理
yii\web\Response::$stream
和yii\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
,sendStreamAsFile
-
yii\web\Request::$methodParam
支持。(实际上不适用于ServerRequestInterface
) - 测试覆盖率
本项目受 BSD-3-Clause 许可协议许可。有关更多详细信息,请参阅 LICENSE。