RFC: PSR-7 continuation

I spoke to @christianm again and we talked about a backwards compatible(!) solution for the next LTS that already gives people the chance to adjust their code to (or at least get acquainted with) the “new way of doing things” with Neos 5.0:

  • We don’t add HttpRequestHandlerInterface::getComponentContext() just yet¹, but instead only deprecate HttpRequestHandlerInterface:: getHttpRequest() and HttpRequestHandlerInterface:: getHttpResponse() with an explanation to work around this dependency (see below)
  • We add another method setComponentParameter() to the ActionResponse that allows for “unplanned extensibility”.

The resulting signature of the ActionResponse would then be sth like:

final class ActionResponse
{
   public function setContent(string $content): void

   public function setContentType(string $contentType): void

   public function setRedirectUri(UriInterface $uri, int $statusCode = 303): void

   public function setStatusCode(int $statusCode): void

   /** new **/
   public function setComponentParameter(string $componentClassName, string $parameterName, $value): void
}

With that in place, people could still mess with the HTTP request/response as much as they want, but via the ActionResponse:

$this->response->setComponentParameter(MyComponent::class, 'setMySpecialHeader', 'foo');

and write a corresponding HTTP Component:

final class MyComponent implements ComponentInterface
{

    public function handle(ComponentContext $componentContext)
    {
        $specialHeader = $componentContext->getParameter(static::class, 'setMySpecialHeader');
        if ($specialHeader !== null) {
            $modifiedResponse = $componentContext->getHttpResponse()->withAddedHeader('X-My-Header', $specialHeader);
            $componentContext->replaceHttpResponse($modifiedResponse);
        }
    }
}

And, in response to some of @aberl’s comments:

Yes, good point, but I still think that we should not encourage that kind of architecture: For middleware kind of cases MVC is not the matching pattern IMO, a custom HTTP component is much better suited.
Having said that, with the above it would be possible to replace the whole Response ofc:

$this->response->setComponentParameter(MyComponent::class, 'replaceHttpResponse', $myHttpResponse);

Sorry, I should have explained myself a bit clearer.
Controller::action(ActionRequest): ActionResponse
would make a lot of sense and in case of a redirect that could be a special RedirectResponse like you suggested.
But we currently have

Dispatcher::dispatch(RequestInterface $request, ResponseInterface $response): void

currently and solve redirects via an endless loop and exceptions.
I’m all for changing that at some point, but it would be yet another major breaking change.


¹ I don’t think that we need to re-introduce a way to get hold of the HTTP request/response or ComponentContext at all. With the extension mentioned above this should not be required. And (because it’s no interface) we could always extend the ActionResponse to provide more features out of the box (like “E-Tag”, “Content lifetime”, …)

1 Like