yoozoo / yii2-psr7-bridge
Yii2 的 PSR7 桥接器
Requires
- psr/http-message: ^1.0
- psr/http-server-handler: ^1.0
- spiral/roadrunner: ^1.2
- yiisoft/yii2: ^2.0.5
- zendframework/zend-diactoros: ^2.0
Requires (Dev)
- monolog/monolog: ^1.24
- phpunit/phpunit: ^7.0.2
- samdark/yii2-psr-log-target: ^1.0
- squizlabs/php_codesniffer: ^3.2
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->request
和 Yii::$app->response
的任何调用)最小代码更改。
请注意,目前该桥接器的质量非常初级。它“工作”是因为可以接受 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')); // 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
和从其派生的类,但有一些注意事项。
- 应用程序组件在运行时添加以下会话 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
实现,您可以在任何自定义响应类中作为 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\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 方法将返回 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
。 - 支持
yii\web\Request::$methodParam
。 (实际上不适用于ServerRequestInterface
) - 测试覆盖率
本项目采用BSD-3-Clause许可。有关更多详细信息,请参阅LICENSE。