Deprecation: #92784 - Extbase controller actions must return ResponseInterface

See forge#92784

Description

Until now, Extbase controller actions could return either nothing (void), null, a string, or an object that implements __toString().

From now on Extbase expects actions to return an instance of Psr\Http\Message\ResponseInterface.

Impact

All actions that do not return an instance of Psr\Http\Message\ResponseInterface trigger a PHP E_USER_DEPRECATED error and will fail as of TYPO3 v12.

Affected Installations

All installations that use Extbase controller actions which don't return an instance of Psr\Http\Message\ResponseInterface.

Migration

Since the core follows not only PSR-7 (https://www.php-fig.org/psr/psr-7/) but also PSR-17 (https://www.php-fig.org/psr/psr-17/), the PSR-17 factories should be used. Both the $responseFactory as well as the $streamFactory are available in all extbase controllers. The $responseFactory can be used to create a blank response object whose content and headers can be set freely. The content can therfore be set using the $streamFactory.

Example:

public function listAction(): ResponseInterface
{
    $items = $this->itemRepository->findAll();
    $this->view->assign('items', $items);

    return $this->responseFactory->createResponse()
        ->withAddedHeader('Content-Type', 'text/html; charset=utf-8')
        ->withBody($this->streamFactory->createStream($this->view->render()));
}
Copied!

This example only shows the most common use case. It causes html with a Content-Type: text/html header and http code 200 Ok returned as the response to the client.

Of course you are free to adjust this response object before returning it.

Example:

public function listAction(): ResponseInterface
{
    $items = $this->itemRepository->findAll();
    $this->view->assign('items', $items);

    return $this->responseFactory
        ->createResponse()
        ->withHeader('Cache-Control', 'must-revalidate')
        ->withHeader('Content-Type', 'text/html; charset=utf-8')
        ->withStatus(200, 'Super ok!')
        ->withBody($this->streamFactory->createStream($this->view->render()));
}
Copied!

In case you are using the JsonView in your extbase controller, you may want to ease the migration path with the new jsonResponse(string $json = null) method. Similar to htmlResponse(), this method creates a PSR-7 Response with the Content-Type: application/json header and http code 200 Ok. If argument $json is omitted, the current view is rendered automatically.

Example:

public function listApiAction(): ResponseInterface
{
    $items = $this->itemRepository->findAll();
    $this->view->assign('value', [
         'items' => $items
     ]);

    return $this->jsonResponse();
}
Copied!

Above example is equivalent to:

public function listApiAction(): ResponseInterface
{
    $items = $this->itemRepository->findAll();
    $this->view->assign('value', [
         'items' => $items
     ]);

    return $this->responseFactory
        ->createResponse()
        ->withHeader('Content-Type', 'application/json; charset=utf-8')
        ->withBody($this->streamFactory->createStream($this->view->render()));
}
Copied!