Skip to content

Commit

Permalink
[FEATURE] Introduce ForwardResponse for extbase
Browse files Browse the repository at this point in the history
This patch introduces a PSR-7 compatible response class
which allows users to initiate forwarding to another
extbase controller action.

Returning a ForwardResponse replaces the helper function
forward() in the ActionController.

Releases: master
Resolves: #92815
Change-Id: I37b40d9e3de1125c0173d2115e0224cb1b13dc2f
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/66564
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Torben Hansen <derhansen@gmail.com>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Torben Hansen <derhansen@gmail.com>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Benni Mack <benni@typo3.org>
  • Loading branch information
alexanderschnitzler authored and bmack committed Dec 5, 2020
1 parent 9e28341 commit 785ff68
Show file tree
Hide file tree
Showing 21 changed files with 445 additions and 97 deletions.
Expand Up @@ -35,6 +35,7 @@
use TYPO3\CMS\Core\Session\SessionManager;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\HttpUtility;
use TYPO3\CMS\Extbase\Http\ForwardResponse;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
Expand Down Expand Up @@ -247,7 +248,7 @@ public function compareAction(): ResponseInterface
*
* @param int $user
*/
public function initiatePasswordResetAction(int $user): void
public function initiatePasswordResetAction(int $user): ResponseInterface
{
$context = GeneralUtility::makeInstance(Context::class);
/** @var BackendUser $user */
Expand All @@ -271,7 +272,7 @@ public function initiatePasswordResetAction(int $user): void
FlashMessage::OK
);
}
$this->forward('index');
return new ForwardResponse('index');
}

/**
Expand All @@ -283,7 +284,7 @@ public function addToCompareListAction($uid)
{
$this->moduleData->attachUidCompareUser($uid);
$this->moduleDataStorageService->persistModuleData($this->moduleData);
$this->forward('index');
return new ForwardResponse('index');
}

/**
Expand Down Expand Up @@ -331,7 +332,7 @@ protected function terminateBackendUserSessionAction(BackendUser $backendUser, $
if ($success) {
$this->addFlashMessage(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:terminateSessionSuccess', 'beuser') ?? '');
}
$this->forward('online');
return new ForwardResponse('online');
}

/**
Expand Down
@@ -0,0 +1,74 @@
.. include:: ../../Includes.txt

===============================================================
Deprecation: #92815 - ActionController::forward() is deprecated
===============================================================

See :issue:`92815`

Description
===========

Method :php:`TYPO3\\CMS\\Extbase\\Mvc\\Controller\\ActionController::forward()` is deprecated in favor of returning a :php:`TYPO3\\CMS\\Extbase\\Http\\ForwardResponse` in a controller action instead.


Impact
======

Calling :php:`TYPO3\\CMS\\Extbase\\Mvc\\Controller\\ActionController::forward()`, which itself throws a `TYPO3\\CMS\\Extbase\\Mvc\\Exception\\StopActionException` to initiate the abortion of the current request and to initiate a new request, will trigger a deprecation warning.


Affected Installations
======================

All installations that make use of the helper method :php:`TYPO3\\CMS\\Extbase\\Mvc\\Controller\\ActionController::forward()`.


Migration
=========

Instead of calling this helper method, a controller action must return a :php:`TYPO3\\CMS\\Extbase\\Http\\ForwardResponse`.

Example:

.. code-block:: php
<?php
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class FooController extends ActionController
{
public function listAction()
{
// do something
$this->forward('show');
}
// more actions here
}
.. code-block:: php
<?php
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Http\ForwardResponse;
class FooController extends ActionController
{
public function listAction(): ResponseInterface
{
// do something
return new ForwardResponse('show');
}
// more actions here
}
.. index:: PHP-API, NotScanned, ext:extbase
@@ -0,0 +1,69 @@
.. include:: ../../Includes.txt

=======================================================
Feature: #92815 - Introduce ForwardResponse for extbase
=======================================================

See :issue:`92815`

Description
===========

Since TYPO3 11.0, extbase controller actions can and should return PSR-7 compatible response objects. To allow the initiation of forwarding to another controller action class :php:`TYPO3\\CMS\\Extbase\\Http\\ForwardResponse` has been introduced.

Minimal example:

.. code-block:: php
<?php
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Http\ForwardResponse;
class FooController extends ActionController
{
public function listAction(): ResponseInterface
{
// do something
return new ForwardResponse('show');
}
public function showAction(): ResponseInterface
{
// do something
}
}
Example that shows the full api:

.. code-block:: php
<?php
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Http\ForwardResponse;
class FooController extends ActionController
{
public function listAction(): ResponseInterface
{
// do something
return (new ForwardResponse('show'))
->withControllerName('Bar')
->withExtensionName('Baz')
->withArguments(['foo' => 'bar'])
;
}
}
Impact
======

Class :php:`TYPO3\\CMS\\Extbase\\Http\\ForwardResponse` allows users to initiate forwarding to other controller actions with a PSR-7 compatible response object.

.. index:: PHP-API, ext:extbase
Expand Up @@ -18,6 +18,7 @@
use OliverHader\IrreTutorial\Service\QueueService;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Http\ForwardResponse;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
use TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface;
Expand Down Expand Up @@ -95,9 +96,10 @@ protected function process($value)
{
if ($this->getQueueService()->isActive()) {
$this->getQueueService()->addValue($this->getRuntimeIdentifier(), $value);
$this->forward('process', 'Queue');
return (new ForwardResponse('process'))->withControllerName('Queue');
}
$this->view->assign('value', $value);
return $this->view->render();
}

/**
Expand Down
Expand Up @@ -39,7 +39,7 @@ public function listAction()
{
$contents = $this->contentRepository->findAll();
$value = $this->getStructure($contents);
$this->process($value);
return $this->process($value);
}

/**
Expand All @@ -48,7 +48,7 @@ public function listAction()
public function showAction(Content $content)
{
$value = $this->getStructure($content);
$this->process($value);
return $this->process($value);
}

/**
Expand Down
Expand Up @@ -16,6 +16,7 @@
namespace OliverHader\IrreTutorial\Controller;

use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Http\ForwardResponse;
use TYPO3\CMS\Extbase\Mvc\Controller\ControllerInterface;
use TYPO3\CMS\Extbase\Mvc\Exception\InvalidControllerException;
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
Expand Down Expand Up @@ -47,18 +48,27 @@ public function indexAction()
$calls[] = ['Content', 'show', ['content' => (string)$uid]];
}
$this->getQueueService()->set($calls);
$this->forward('process');
return new ForwardResponse('process');
}

public function processAction()
{
$call = $this->getQueueService()->shift();
if ($call === null) {
$this->forward('finish');
return new ForwardResponse('finish');
}
// Clear these states and fetch fresh entities!
$this->getPersistenceManager()->clearState();
$this->forward($call[1], $call[0], null, $call[2] ?? null);

$response = (new ForwardResponse($call[1]))
->withControllerName($call[0]);

$arguments = $call[2] ?? null;
if (is_array($arguments)) {
$response = $response->withArguments($arguments);
}

return $response;
}

public function finishAction()
Expand Down
111 changes: 111 additions & 0 deletions typo3/sysext/extbase/Classes/Http/ForwardResponse.php
@@ -0,0 +1,111 @@
<?php

declare(strict_types=1);

/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/

namespace TYPO3\CMS\Extbase\Http;

use TYPO3\CMS\Core\Http\Response;
use TYPO3\CMS\Extbase\Error\Result;

class ForwardResponse extends Response
{
private string $actionName;
private ?string $controllerName = null;
private ?string $extensionName = null;
private array $arguments = [];
private Result $argumentsValidationResult;

public function __construct(string $actionName)
{
$this->actionName = $actionName;
$this->argumentsValidationResult = new Result();
parent::__construct('php://temp', 204);
}

public function withControllerName(string $controllerName): self
{
$clone = clone $this;
$clone->controllerName = $controllerName;
return $clone;
}

public function withoutControllerName(): self
{
$clone = clone $this;
$clone->controllerName = null;
return $clone;
}

public function withExtensionName(string $extensionName): self
{
$clone = clone $this;
$clone->extensionName = $extensionName;
return $clone;
}

public function withoutExtensionName(): self
{
$clone = clone $this;
$this->extensionName = null;
return $clone;
}

public function withArguments(array $arguments): self
{
$clone = clone $this;
$clone->arguments = $arguments;
return $clone;
}

public function withoutArguments(): self
{
$clone = clone $this;
$this->arguments = [];
return $clone;
}

public function withArgumentsValidationResult(Result $argumentsValidationResult): self
{
$clone = clone $this;
$clone->argumentsValidationResult = $argumentsValidationResult;
return $clone;
}

public function getActionName(): string
{
return $this->actionName;
}

public function getControllerName(): ?string
{
return $this->controllerName;
}

public function getExtensionName(): ?string
{
return $this->extensionName;
}

public function getArguments(): array
{
return $this->arguments;
}

public function getArgumentsValidationResult(): Result
{
return $this->argumentsValidationResult;
}
}

0 comments on commit 785ff68

Please sign in to comment.