brnc/psr7-symfony1-adapter

适用于Symfony 1.5的部分PSR-7适配器

v1.6.1 2024-06-07 14:24 UTC

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\ResponseFactoryInterfaceResponseFactory来在您的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的来源实现AbstractCookieDispatchTranscriptortranscribeCookies()方法。例如,如果您使用第三方库。您的代码最终需要返回一个充满CookieInterfaceCookieContainerInterface。已经有一个HeaderCookie,它使用header()并期望一个已经制作好的和完整的Set-Cookie-头行。还有SetCookieSetRawCookie,它们将使用具有选项数组作为第三个参数的新签名的方法。

将其传递到 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!