ebln / psr7-symfony1-adapter
Symfony 1.5 部分PSR-7适配器
Requires
- php: ^7.4 || ^8.0
- guzzlehttp/psr7: ^2.4.5
- psr/http-factory: ^1.0
- psr/http-message: ^1.1 || ^2.0
- webmozart/assert: ^1.11
Requires (Dev)
- composer/package-versions-deprecated: ^1.11
- ergebnis/composer-normalize: ^2.28
- php-http/psr7-integration-tests: ^1.3
- phpmd/phpmd: ^2.13
- phpstan/phpstan: ^1.11
- phpstan/phpstan-webmozart-assert: ^1.2
- phpunit/phpunit: ^9.6
- roave/security-advisories: dev-latest
- vimeo/psalm: ^5.13
Replaces
- brnc/psr7-symfony1-adapter: 1.6.0
README
为了通过部分PSR-7适配器启用未来兼容的PSR-15中间件。
快速入门
// not fully PSR-7 compliant lazy adapters $serverRequestAdapter = \brnc\Symfony1\Message\Adapter\Request::fromSfWebRequest($sfWebRequest); $responseAdapter = \brnc\Symfony1\Message\Adapter\Response::fromSfWebResponse($sfWebResponse);
ServerRequest
请注意以下默认启用的PSR-7违规行为
默认情况下不保持不可变
因为这只是一个针对 \sfWebRequest
的适配器,无法轻松用另一个实例替换。
此适配器默认情况下在调用 with*()
方法时也会返回完全相同的实例。出于相同的原因,调用无法对底层 \sfWebRequest
对象进行操作的 with*()
方法将引发 \brnc\Symfony1\Message\Exception\LogicException
。
可以通过使用具有 Request::OPTION_IMMUTABLE_VIOLATION
选项设置为 false
的 Request
来更改此默认行为。然后,当调用 with*()
-方法时,Request
-适配器将始终返回新的实例,并且不会在无法透明地操作 \sfWebRequest
对象的调用上抛出异常。
use brnc\Symfony1\Message\Adapter\Request; $serverRequestAdapter = Request::fromSfWebRequest( $sfWebRequest, [ // If set to true a stream on php://input is used instead of creating one over sfWebRequest::getContent() → defaults to false Request::OPTION_BODY_USE_STREAM => false, // sfWebRequest-compatibility mode – set to false if you need PSR-7's immutability Request::OPTION_IMMUTABLE_VIOLATION => true, ] );
Response
请注意默认的可变性
use brnc\Symfony1\Message\Adapter\Response; $responseAdapter = Response::fromSfWebResponse( $sfWebResponse, [Response::OPTION_IMMUTABLE_VIOLATION => false] ); $newInstance = $responseAdapter->withBody( \GuzzleHttp\Psr7\Utils::streamFor( '<html><head><title>Hello World!</title></head><body><h1>PSR-7 Adapters!</h1></body></html>' ) ); $newestInstance = $newInstance->withBody( \GuzzleHttp\Psr7\Utils::streamFor( '<html><head><body><h1>dead end</h1></body></html>' ) ); // selects the content of $newInstance to be send instead of the most recent instance's one (i.e. $newestInstance) $newInstance->preSend(); // N.b. The stream of $newestInstance is still held in memory until $responseAdapter and all copies got destroyed! // This might change in the future when this will be refactored to use WeakMap. $sfWebResponse->send();
将其传递给PSR-15子栈
您可以使用实现 \Psr\Http\Message\ResponseFactoryInterface
的 ResponseFactory
来在您的PSR-15子栈中“产生”响应。
$request = \brnc\Symfony1\Message\Adapter\Request::fromSfWebRequest($sfWebRequest); $responseFactory = new \brnc\Symfony1\Message\Factory\ResponseFactory($sfWebResponse); // (dependency) inject the ResponseFactory to your dispatcher, middlewares, and handlers $entryPoint = new YourPSR15Dispatcher($responseFactory); // Dispatch your sub-stack via PSR-15 $response = $entryPoint->handler($response); // As $response will be linked to $sfWebResponse you don't need to do anything // if you are in the context of a Symfony1 action. Only call $response->getSfWebResponse() in dire need!
手动将PSR-7响应转录到Symfony1
假设您无法使用其他方法,并且您面临一个任意的PSR-7响应,您可以使用 ResponseTranscriptor
将数据从您的PSR-7响应复制到您的 \sfWebResponse
。
ResponseTranscriptor
默认使用 NoCookieTranscriptor
,这会在存在 Set-Cookie'
标头的情况下失败。将(现在的)Cookie合并到 \sfWebResponse
中不是一件简单的事情。然而,您可以自由实现自己的实现 CookieTranscriptorInterface
的Cookie处理器,并将其作为可选的构造函数参数传递。
// Given arbitrary PSR-7 response… $psr7response = $psr7responseFactory(); // …use the ResponseTranscriptor in order to– $transcriptor = new \brnc\Symfony1\Message\Transcriptor\ResponseTranscriptor(); // copy the response's contents. // The returned object will be the same as in the argument! $sfWebResponse = $transcriptor->transcribe($psr7response, $sfWebResponse);
实现的 CookieTranscriptorInterface
已经实现了一些CookieTranscriptors;每个都有其特定的权衡。
CookieHeaderTranscriptor
将您的PSR-7响应中的 Set-Cookie
标头转录到Symfony1响应的Cookie管理中。这带来了所有遗留签名 setrawcookie()
的缺点。首先,它不支持 SameSite
-属性,以及其他所有作为 extension-av
的内容,按照RFC 265的规定。
AbstractCookieDispatchTranscriptor
(抽象) CookieDispatchTranscriptor 使用反射并交换响应的事件调度器。它与原始的 sfWebResponse::sendHttpHeaders
实现非常紧密,特别是通过事件进行日志记录的机制。CookieDispatcher
在 sfWebResponse
和原始 sfEventDispatcher
之间插入自己,并在Symfony1发送其自己的Cookie之前触发PSR-7响应中的Cookie。您需要根据设置Cookie的来源实现 AbstractCookieDispatchTranscriptor
的 transcribeCookies()
方法。例如,如果您使用第三方库。您的代码最终需要返回一个包含 CookieInterface
的 CookieContainerInterface
。已经有一个 HeaderCookie
,它使用 header()
并期望一个已经制作好并且完整的 Set-Cookie
-标头行。还有 SetCookie
和 SetRawCookie
,它们将使用具有新签名的相应方法——即三个参数,其中选项数组作为第三个参数。
将其传递给 http-foundation,即现在的Symfony
将此PSR7-Symfony1适配器与 symfony/psr-http-message-bridge
结合使用,通过PSR-7将您的Symfony1堆栈连接到 symfony/http-foundation
对象,并利用使用嵌入(现在的)Symfony组件。
// Use this chain to create a http-foundation request from a Symfony1's \sfWebRequest $psrRequest = \brnc\Symfony1\Message\Adapter\Request::fromSfWebRequest($sfWebRequest); $httpFoundationFactory = \Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory(); $symfonyRequest = $httpFoundationFactory->createRequest($psrRequest); // Handle the request with some present day Symfony component $symfonyResponse = $httpKernel->handle($symfonyRequest); // Possibly ResponseFactory is best created in the Symfony1 context $responseFactory = new \brnc\Symfony1\Message\Factory\ResponseFactory($sfWebResponse); // Obtain other PSR17 factories, // while only ResponseFactory & StreamFactory will be used (as of today) $streamFactory = \brnc\Symfony1\Message\Factory\GuzzleStreamFactory(); $decoyFactory = \brnc\Symfony1\Message\Factory\DecoyHttpFactory(); // Construct the PsrHttpFactory from symfony/psr-http-message-bridge and translate… $psrHttpFactory = Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory( $decoyFactory, $streamFactory, $decoyFactory, $responseFactory ); $psrResponse = $psrHttpFactory->createResponse($symfonyResponse); // As $psrResponse will be linked to $sfWebResponse as it was created through the // ResponseFactory you don't need to do anything if you exit via an Symfony1 action. // Only call $psrResponse->getSfWebResponse() in dire need!