brnc / 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
对象进行操作的方法的调用将抛出\brnc\Symfony1\Message\Exception\LogicException
。
可以通过使用Request::OPTION_IMMUTABLE_VIOLATION
选项并将它设置为false
来更改此默认行为。然后,当调用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
头的情况下会失败。将(现在的)Cookies合并到\sfWebResponse
中并不简单。然而,您可以根据需要实现自己的Cookie-Handler实现CookieTranscriptorInterface
,并将其作为可选构造函数参数传递。
// 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!