From 93ba58d023c496c6b4b61672172b6329961e9999 Mon Sep 17 00:00:00 2001
From: mhsdesign <85400359+mhsdesign@users.noreply.github.com>
Date: Sat, 27 Jan 2024 22:49:39 +0100
Subject: [PATCH 01/10] WIP: Make ViewInterface independent of controller
context
---
.../Mvc/Controller/ActionController.php | 5 ++-
Neos.Flow/Classes/Mvc/View/AbstractView.php | 10 ++++-
Neos.Flow/Classes/Mvc/View/JsonView.php | 6 +--
Neos.Flow/Classes/Mvc/View/ViewInterface.php | 5 ++-
.../Fixtures/TemplateView.php | 11 ------
.../Mvc/Controller/ActionControllerTest.php | 37 ++-----------------
6 files changed, 19 insertions(+), 55 deletions(-)
diff --git a/Neos.Flow/Classes/Mvc/Controller/ActionController.php b/Neos.Flow/Classes/Mvc/Controller/ActionController.php
index 7ebb5aa034..fa50a65f61 100644
--- a/Neos.Flow/Classes/Mvc/Controller/ActionController.php
+++ b/Neos.Flow/Classes/Mvc/Controller/ActionController.php
@@ -246,7 +246,10 @@ public function processRequest(ActionRequest $request, ActionResponse $response)
}
if ($this->view !== null) {
$this->view->assign('settings', $this->settings);
- $this->view->setControllerContext($this->controllerContext);
+ $this->view->assign('request', $this->request);
+ if (method_exists($this->view, 'setControllerContext')) {
+ $this->view->setControllerContext($this->controllerContext);
+ }
$this->initializeView($this->view);
}
diff --git a/Neos.Flow/Classes/Mvc/View/AbstractView.php b/Neos.Flow/Classes/Mvc/View/AbstractView.php
index 858d11ee70..08e2d9583b 100644
--- a/Neos.Flow/Classes/Mvc/View/AbstractView.php
+++ b/Neos.Flow/Classes/Mvc/View/AbstractView.php
@@ -11,8 +11,11 @@
* source code.
*/
+use Neos\Flow\Mvc\ActionResponse;
+use Neos\Flow\Mvc\Controller\Arguments;
use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\Mvc\Exception;
+use Neos\Flow\Mvc\Routing\UriBuilder;
/**
* An abstract View
@@ -52,6 +55,8 @@ abstract class AbstractView implements ViewInterface
/**
* @var ControllerContext
+ * @deprecated if you absolutely need access to the current request please assign a variable.
+ * when using the action controller the request is directly available at "request"
*/
protected $controllerContext;
@@ -168,9 +173,10 @@ public function assignMultiple(array $values)
/**
* Sets the current controller context
*
- * @param ControllerContext $controllerContext
+ * @deprecated if you absolutely need access to the current request please assign a variable.
+ * when using the action controller the request is directly available at "request"
+ * @param ControllerContext $controllerContext Context of the controller associated with this view
* @return void
- * @api
*/
public function setControllerContext(ControllerContext $controllerContext)
{
diff --git a/Neos.Flow/Classes/Mvc/View/JsonView.php b/Neos.Flow/Classes/Mvc/View/JsonView.php
index 40844a72e2..250bb77065 100644
--- a/Neos.Flow/Classes/Mvc/View/JsonView.php
+++ b/Neos.Flow/Classes/Mvc/View/JsonView.php
@@ -50,11 +50,6 @@ class JsonView extends AbstractView
*/
const EXPOSE_CLASSNAME_UNQUALIFIED = 2;
- /**
- * @var ControllerContext
- */
- protected $controllerContext;
-
/**
* Only variables whose name is contained in this array will be rendered
*
@@ -200,6 +195,7 @@ public function setConfiguration(array $configuration)
*/
public function render()
{
+ // todo how to support this?
$this->controllerContext->getResponse()->setContentType('application/json');
$propertiesToRender = $this->renderArray();
$options = $this->getOption('jsonEncodingOptions');
diff --git a/Neos.Flow/Classes/Mvc/View/ViewInterface.php b/Neos.Flow/Classes/Mvc/View/ViewInterface.php
index 45f544256a..8a1422bc83 100644
--- a/Neos.Flow/Classes/Mvc/View/ViewInterface.php
+++ b/Neos.Flow/Classes/Mvc/View/ViewInterface.php
@@ -26,11 +26,12 @@ interface ViewInterface
/**
* Sets the current controller context
*
+ * @deprecated if you absolutely need access to the current request please assign a variable.
+ * when using the action controller the request is directly available at "request"
* @param ControllerContext $controllerContext Context of the controller associated with this view
* @return void
- * @api
*/
- public function setControllerContext(ControllerContext $controllerContext);
+ // public function setControllerContext(ControllerContext $controllerContext);
/**
* Add a variable to the view data collection.
diff --git a/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php b/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php
index 3fa5e5b4ef..73a7cc2bc5 100644
--- a/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php
+++ b/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php
@@ -39,17 +39,6 @@ final class TemplateView extends AbstractView
'layoutPathAndFilename' => [null, 'Path and filename of the layout file. If set, overrides the layoutPathAndFilenamePattern', 'string'],
];
- /**
- * Dummy method to satisfy the ViewInterface
- *
- * @param ControllerContext $controllerContext
- * @return void
- * @api
- */
- public function setControllerContext(ControllerContext $controllerContext)
- {
- }
-
/**
* Dummy method to satisfy the ViewInterface
*
diff --git a/Neos.Flow/Tests/Unit/Mvc/Controller/ActionControllerTest.php b/Neos.Flow/Tests/Unit/Mvc/Controller/ActionControllerTest.php
index 523ece5c81..e8b7c58465 100644
--- a/Neos.Flow/Tests/Unit/Mvc/Controller/ActionControllerTest.php
+++ b/Neos.Flow/Tests/Unit/Mvc/Controller/ActionControllerTest.php
@@ -201,45 +201,14 @@ public function processRequestThrowsExceptionIfRequestedActionIsNotPublic()
/**
* @test
*/
- public function processRequestInjectsControllerContextToView()
- {
- $this->actionController = $this->getAccessibleMock(ActionController::class, ['resolveActionMethodName', 'initializeActionMethodArguments', 'initializeActionMethodValidators', 'resolveView', 'callActionMethod', 'initializeController']);
- $this->actionController->method('resolveActionMethodName')->willReturn('indexAction');
-
- $this->inject($this->actionController, 'objectManager', $this->mockObjectManager);
- $this->inject($this->actionController, 'controllerContext', $this->mockControllerContext);
- $this->inject($this->actionController, 'request', $this->mockRequest);
-
- $this->inject($this->actionController, 'arguments', new Arguments([]));
-
- $mockMvcPropertyMappingConfigurationService = $this->createMock(Mvc\Controller\MvcPropertyMappingConfigurationService::class);
- $this->inject($this->actionController, 'mvcPropertyMappingConfigurationService', $mockMvcPropertyMappingConfigurationService);
-
- $mockHttpRequest = $this->getMockBuilder(ServerRequestInterface::class)->disableOriginalConstructor()->getMock();
- $this->mockRequest->expects(self::any())->method('getHttpRequest')->will(self::returnValue($mockHttpRequest));
-
- $mockResponse = new Mvc\ActionResponse;
- $mockResponse->setContentType('text/plain');
- $this->inject($this->actionController, 'response', $mockResponse);
-
- $mockView = $this->createMock(Mvc\View\ViewInterface::class);
- $mockView->expects(self::once())->method('setControllerContext')->with($this->mockControllerContext);
- $this->actionController->expects(self::once())->method('resolveView')->will(self::returnValue($mockView));
- $this->actionController->expects(self::once())->method('resolveActionMethodName')->will(self::returnValue('someAction'));
-
- $this->actionController->processRequest($this->mockRequest, $mockResponse);
- }
-
- /**
- * @test
- */
- public function processRequestInjectsSettingsToView()
+ public function processRequestInjectsSettingsAndRequestToView()
{
$this->actionController = $this->getAccessibleMock(ActionController::class, ['resolveActionMethodName', 'initializeActionMethodArguments', 'initializeActionMethodValidators', 'resolveView', 'callActionMethod']);
$this->actionController->method('resolveActionMethodName')->willReturn('indexAction');
$this->inject($this->actionController, 'objectManager', $this->mockObjectManager);
$this->inject($this->actionController, 'controllerContext', $this->mockControllerContext);
+ $this->inject($this->actionController, 'request', $this->mockRequest);
$mockSettings = ['foo', 'bar'];
$this->inject($this->actionController, 'settings', $mockSettings);
@@ -253,7 +222,7 @@ public function processRequestInjectsSettingsToView()
$mockResponse = new Mvc\ActionResponse;
$mockView = $this->createMock(Mvc\View\ViewInterface::class);
- $mockView->expects(self::once())->method('assign')->with('settings', $mockSettings);
+ $mockView->expects(self::exactly(2))->method('assign')->withConsecutive(['settings', $mockSettings], ['request', $this->mockRequest]);
$this->actionController->expects(self::once())->method('resolveView')->will(self::returnValue($mockView));
$this->actionController->expects(self::once())->method('resolveActionMethodName')->will(self::returnValue('someAction'));
From 3afca62ba09afee33095d3caef80ea208f7eec9a Mon Sep 17 00:00:00 2001
From: mhsdesign <85400359+mhsdesign@users.noreply.github.com>
Date: Sat, 27 Jan 2024 23:02:59 +0100
Subject: [PATCH 02/10] WIP: Introduce ActionMessages tuple to replace
controller context
---
Neos.Flow/Classes/Mvc/ActionMessages.php | 19 +++++++++++++++++++
.../Mvc/Controller/ActionController.php | 3 ++-
Neos.Flow/Classes/Mvc/View/AbstractView.php | 2 +-
Neos.Flow/Classes/Mvc/View/JsonView.php | 8 +++++---
Neos.Flow/Classes/Mvc/View/ViewInterface.php | 2 +-
.../Mvc/Controller/ActionControllerTest.php | 2 +-
.../Tests/Unit/Mvc/View/JsonViewTest.php | 2 +-
7 files changed, 30 insertions(+), 8 deletions(-)
create mode 100644 Neos.Flow/Classes/Mvc/ActionMessages.php
diff --git a/Neos.Flow/Classes/Mvc/ActionMessages.php b/Neos.Flow/Classes/Mvc/ActionMessages.php
new file mode 100644
index 0000000000..5f2bfe652e
--- /dev/null
+++ b/Neos.Flow/Classes/Mvc/ActionMessages.php
@@ -0,0 +1,19 @@
+view !== null) {
$this->view->assign('settings', $this->settings);
- $this->view->assign('request', $this->request);
+ $this->view->assign('actionMessages', ActionMessages::create($this->request, $this->response));
if (method_exists($this->view, 'setControllerContext')) {
$this->view->setControllerContext($this->controllerContext);
}
diff --git a/Neos.Flow/Classes/Mvc/View/AbstractView.php b/Neos.Flow/Classes/Mvc/View/AbstractView.php
index 08e2d9583b..7d845d03fe 100644
--- a/Neos.Flow/Classes/Mvc/View/AbstractView.php
+++ b/Neos.Flow/Classes/Mvc/View/AbstractView.php
@@ -174,7 +174,7 @@ public function assignMultiple(array $values)
* Sets the current controller context
*
* @deprecated if you absolutely need access to the current request please assign a variable.
- * when using the action controller the request is directly available at "request"
+ * when using the action controller the request is directly available at "actionMessages"
* @param ControllerContext $controllerContext Context of the controller associated with this view
* @return void
*/
diff --git a/Neos.Flow/Classes/Mvc/View/JsonView.php b/Neos.Flow/Classes/Mvc/View/JsonView.php
index 250bb77065..dc6ff94849 100644
--- a/Neos.Flow/Classes/Mvc/View/JsonView.php
+++ b/Neos.Flow/Classes/Mvc/View/JsonView.php
@@ -13,7 +13,7 @@
*/
use Neos\Flow\Annotations as Flow;
-use Neos\Flow\Mvc\Controller\ControllerContext;
+use Neos\Flow\Mvc\ActionMessages;
use Neos\Flow\Persistence\PersistenceManagerInterface;
use Neos\Utility\ObjectAccess;
use Neos\Utility\TypeHandling;
@@ -195,8 +195,10 @@ public function setConfiguration(array $configuration)
*/
public function render()
{
- // todo how to support this?
- $this->controllerContext->getResponse()->setContentType('application/json');
+ /** @var ActionMessages $actionMessages */
+ $actionMessages = $this->variables['actionMessages'];
+ $actionMessages->response->setContentType('application/json');
+
$propertiesToRender = $this->renderArray();
$options = $this->getOption('jsonEncodingOptions');
return json_encode($propertiesToRender, JSON_THROW_ON_ERROR | $options);
diff --git a/Neos.Flow/Classes/Mvc/View/ViewInterface.php b/Neos.Flow/Classes/Mvc/View/ViewInterface.php
index 8a1422bc83..6e33f62d87 100644
--- a/Neos.Flow/Classes/Mvc/View/ViewInterface.php
+++ b/Neos.Flow/Classes/Mvc/View/ViewInterface.php
@@ -27,7 +27,7 @@ interface ViewInterface
* Sets the current controller context
*
* @deprecated if you absolutely need access to the current request please assign a variable.
- * when using the action controller the request is directly available at "request"
+ * when using the action controller the request is directly available at "actionMessages"
* @param ControllerContext $controllerContext Context of the controller associated with this view
* @return void
*/
diff --git a/Neos.Flow/Tests/Unit/Mvc/Controller/ActionControllerTest.php b/Neos.Flow/Tests/Unit/Mvc/Controller/ActionControllerTest.php
index e8b7c58465..5ec049a6dc 100644
--- a/Neos.Flow/Tests/Unit/Mvc/Controller/ActionControllerTest.php
+++ b/Neos.Flow/Tests/Unit/Mvc/Controller/ActionControllerTest.php
@@ -222,7 +222,7 @@ public function processRequestInjectsSettingsAndRequestToView()
$mockResponse = new Mvc\ActionResponse;
$mockView = $this->createMock(Mvc\View\ViewInterface::class);
- $mockView->expects(self::exactly(2))->method('assign')->withConsecutive(['settings', $mockSettings], ['request', $this->mockRequest]);
+ $mockView->expects(self::exactly(2))->method('assign')->withConsecutive(['settings', $mockSettings], ['actionMessages', Mvc\ActionMessages::create($this->mockRequest, $mockResponse)]);
$this->actionController->expects(self::once())->method('resolveView')->will(self::returnValue($mockView));
$this->actionController->expects(self::once())->method('resolveActionMethodName')->will(self::returnValue('someAction'));
diff --git a/Neos.Flow/Tests/Unit/Mvc/View/JsonViewTest.php b/Neos.Flow/Tests/Unit/Mvc/View/JsonViewTest.php
index e2d74af3ba..9cd2eeef22 100644
--- a/Neos.Flow/Tests/Unit/Mvc/View/JsonViewTest.php
+++ b/Neos.Flow/Tests/Unit/Mvc/View/JsonViewTest.php
@@ -45,7 +45,7 @@ protected function setUp(): void
$this->controllerContext = $this->getMockBuilder(Mvc\Controller\ControllerContext::class)->disableOriginalConstructor()->getMock();
$this->response = new Mvc\ActionResponse();
$this->controllerContext->expects(self::any())->method('getResponse')->will(self::returnValue($this->response));
- $this->view->setControllerContext($this->controllerContext);
+ $this->view->assign('actionMessages', Mvc\ActionMessages::create($this->getMockBuilder(Mvc\ActionRequest::class)->disableOriginalConstructor()->getMock(), $this->response));
}
/**
From ac8cfc77762a01a6b115883ca3654b24795ed849 Mon Sep 17 00:00:00 2001
From: mhsdesign <85400359+mhsdesign@users.noreply.github.com>
Date: Mon, 29 Jan 2024 16:14:15 +0100
Subject: [PATCH 03/10] Revert "WIP: Introduce ActionMessages tuple to replace
controller context"
This reverts commit 2717a8c19a4a05c5e42a0f85a2a110d74f651acc.
---
Neos.Flow/Classes/Mvc/ActionMessages.php | 19 -------------------
.../Mvc/Controller/ActionController.php | 3 +--
Neos.Flow/Classes/Mvc/View/AbstractView.php | 2 +-
Neos.Flow/Classes/Mvc/View/JsonView.php | 8 +++-----
Neos.Flow/Classes/Mvc/View/ViewInterface.php | 2 +-
.../Mvc/Controller/ActionControllerTest.php | 2 +-
.../Tests/Unit/Mvc/View/JsonViewTest.php | 2 +-
7 files changed, 8 insertions(+), 30 deletions(-)
delete mode 100644 Neos.Flow/Classes/Mvc/ActionMessages.php
diff --git a/Neos.Flow/Classes/Mvc/ActionMessages.php b/Neos.Flow/Classes/Mvc/ActionMessages.php
deleted file mode 100644
index 5f2bfe652e..0000000000
--- a/Neos.Flow/Classes/Mvc/ActionMessages.php
+++ /dev/null
@@ -1,19 +0,0 @@
-view !== null) {
$this->view->assign('settings', $this->settings);
- $this->view->assign('actionMessages', ActionMessages::create($this->request, $this->response));
+ $this->view->assign('request', $this->request);
if (method_exists($this->view, 'setControllerContext')) {
$this->view->setControllerContext($this->controllerContext);
}
diff --git a/Neos.Flow/Classes/Mvc/View/AbstractView.php b/Neos.Flow/Classes/Mvc/View/AbstractView.php
index 7d845d03fe..08e2d9583b 100644
--- a/Neos.Flow/Classes/Mvc/View/AbstractView.php
+++ b/Neos.Flow/Classes/Mvc/View/AbstractView.php
@@ -174,7 +174,7 @@ public function assignMultiple(array $values)
* Sets the current controller context
*
* @deprecated if you absolutely need access to the current request please assign a variable.
- * when using the action controller the request is directly available at "actionMessages"
+ * when using the action controller the request is directly available at "request"
* @param ControllerContext $controllerContext Context of the controller associated with this view
* @return void
*/
diff --git a/Neos.Flow/Classes/Mvc/View/JsonView.php b/Neos.Flow/Classes/Mvc/View/JsonView.php
index dc6ff94849..250bb77065 100644
--- a/Neos.Flow/Classes/Mvc/View/JsonView.php
+++ b/Neos.Flow/Classes/Mvc/View/JsonView.php
@@ -13,7 +13,7 @@
*/
use Neos\Flow\Annotations as Flow;
-use Neos\Flow\Mvc\ActionMessages;
+use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\Persistence\PersistenceManagerInterface;
use Neos\Utility\ObjectAccess;
use Neos\Utility\TypeHandling;
@@ -195,10 +195,8 @@ public function setConfiguration(array $configuration)
*/
public function render()
{
- /** @var ActionMessages $actionMessages */
- $actionMessages = $this->variables['actionMessages'];
- $actionMessages->response->setContentType('application/json');
-
+ // todo how to support this?
+ $this->controllerContext->getResponse()->setContentType('application/json');
$propertiesToRender = $this->renderArray();
$options = $this->getOption('jsonEncodingOptions');
return json_encode($propertiesToRender, JSON_THROW_ON_ERROR | $options);
diff --git a/Neos.Flow/Classes/Mvc/View/ViewInterface.php b/Neos.Flow/Classes/Mvc/View/ViewInterface.php
index 6e33f62d87..8a1422bc83 100644
--- a/Neos.Flow/Classes/Mvc/View/ViewInterface.php
+++ b/Neos.Flow/Classes/Mvc/View/ViewInterface.php
@@ -27,7 +27,7 @@ interface ViewInterface
* Sets the current controller context
*
* @deprecated if you absolutely need access to the current request please assign a variable.
- * when using the action controller the request is directly available at "actionMessages"
+ * when using the action controller the request is directly available at "request"
* @param ControllerContext $controllerContext Context of the controller associated with this view
* @return void
*/
diff --git a/Neos.Flow/Tests/Unit/Mvc/Controller/ActionControllerTest.php b/Neos.Flow/Tests/Unit/Mvc/Controller/ActionControllerTest.php
index 5ec049a6dc..e8b7c58465 100644
--- a/Neos.Flow/Tests/Unit/Mvc/Controller/ActionControllerTest.php
+++ b/Neos.Flow/Tests/Unit/Mvc/Controller/ActionControllerTest.php
@@ -222,7 +222,7 @@ public function processRequestInjectsSettingsAndRequestToView()
$mockResponse = new Mvc\ActionResponse;
$mockView = $this->createMock(Mvc\View\ViewInterface::class);
- $mockView->expects(self::exactly(2))->method('assign')->withConsecutive(['settings', $mockSettings], ['actionMessages', Mvc\ActionMessages::create($this->mockRequest, $mockResponse)]);
+ $mockView->expects(self::exactly(2))->method('assign')->withConsecutive(['settings', $mockSettings], ['request', $this->mockRequest]);
$this->actionController->expects(self::once())->method('resolveView')->will(self::returnValue($mockView));
$this->actionController->expects(self::once())->method('resolveActionMethodName')->will(self::returnValue('someAction'));
diff --git a/Neos.Flow/Tests/Unit/Mvc/View/JsonViewTest.php b/Neos.Flow/Tests/Unit/Mvc/View/JsonViewTest.php
index 9cd2eeef22..e2d74af3ba 100644
--- a/Neos.Flow/Tests/Unit/Mvc/View/JsonViewTest.php
+++ b/Neos.Flow/Tests/Unit/Mvc/View/JsonViewTest.php
@@ -45,7 +45,7 @@ protected function setUp(): void
$this->controllerContext = $this->getMockBuilder(Mvc\Controller\ControllerContext::class)->disableOriginalConstructor()->getMock();
$this->response = new Mvc\ActionResponse();
$this->controllerContext->expects(self::any())->method('getResponse')->will(self::returnValue($this->response));
- $this->view->assign('actionMessages', Mvc\ActionMessages::create($this->getMockBuilder(Mvc\ActionRequest::class)->disableOriginalConstructor()->getMock(), $this->response));
+ $this->view->setControllerContext($this->controllerContext);
}
/**
From d91c6a2707d8e96f7da2379a316d680eca3c2471 Mon Sep 17 00:00:00 2001
From: mhsdesign <85400359+mhsdesign@users.noreply.github.com>
Date: Mon, 29 Jan 2024 16:20:29 +0100
Subject: [PATCH 04/10] !!! TASK JsonView returns ResponseInterface directly
---
Neos.Flow/Classes/Mvc/View/JsonView.php | 13 +++++----
.../Tests/Unit/Mvc/View/JsonViewTest.php | 28 +++++++++----------
2 files changed, 22 insertions(+), 19 deletions(-)
diff --git a/Neos.Flow/Classes/Mvc/View/JsonView.php b/Neos.Flow/Classes/Mvc/View/JsonView.php
index 250bb77065..1dd8f8b6cd 100644
--- a/Neos.Flow/Classes/Mvc/View/JsonView.php
+++ b/Neos.Flow/Classes/Mvc/View/JsonView.php
@@ -12,11 +12,13 @@
* source code.
*/
+use GuzzleHttp\Psr7\Response;
+use GuzzleHttp\Psr7\Utils;
use Neos\Flow\Annotations as Flow;
-use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\Persistence\PersistenceManagerInterface;
use Neos\Utility\ObjectAccess;
use Neos\Utility\TypeHandling;
+use Psr\Http\Message\ResponseInterface;
/**
* A JSON view
@@ -190,16 +192,17 @@ public function setConfiguration(array $configuration)
* array represantion using a YAML view configuration and JSON encodes
* the result.
*
- * @return string The JSON encoded variables
+ * @return ResponseInterface The JSON encoded variables
* @api
*/
public function render()
{
- // todo how to support this?
- $this->controllerContext->getResponse()->setContentType('application/json');
+ $response = new Response();
+ $response = $response->withHeader('Content-Type', 'application/json');
$propertiesToRender = $this->renderArray();
$options = $this->getOption('jsonEncodingOptions');
- return json_encode($propertiesToRender, JSON_THROW_ON_ERROR | $options);
+ $value = json_encode($propertiesToRender, JSON_THROW_ON_ERROR | $options);
+ return $response->withBody(Utils::streamFor($value));
}
/**
diff --git a/Neos.Flow/Tests/Unit/Mvc/View/JsonViewTest.php b/Neos.Flow/Tests/Unit/Mvc/View/JsonViewTest.php
index e2d74af3ba..f187479c8d 100644
--- a/Neos.Flow/Tests/Unit/Mvc/View/JsonViewTest.php
+++ b/Neos.Flow/Tests/Unit/Mvc/View/JsonViewTest.php
@@ -267,7 +267,7 @@ public function renderSetsContentTypeHeader()
{
$this->response->expects(self::once())->method('setHeader')->with('Content-Type', 'application/json');
- $this->view->render();
+ $this->view->render()->getBody()->getContents();
}
/**
@@ -280,7 +280,7 @@ public function renderReturnsJsonRepresentationOfAssignedObject()
$this->view->assign('value', $object);
$expectedResult = '{"foo":"Foo"}';
- $actualResult = $this->view->render();
+ $actualResult = $this->view->render()->getBody()->getContents();
self::assertEquals($expectedResult, $actualResult);
}
@@ -293,7 +293,7 @@ public function renderReturnsJsonRepresentationOfAssignedArray()
$this->view->assign('value', $array);
$expectedResult = '{"foo":"Foo","bar":"Bar"}';
- $actualResult = $this->view->render();
+ $actualResult = $this->view->render()->getBody()->getContents();
self::assertEquals($expectedResult, $actualResult);
}
@@ -306,7 +306,7 @@ public function renderReturnsJsonRepresentationOfAssignedSimpleValue()
$this->view->assign('value', $value);
$expectedResult = '"Foo"';
- $actualResult = $this->view->render();
+ $actualResult = $this->view->render()->getBody()->getContents();
self::assertEquals($expectedResult, $actualResult);
}
@@ -319,7 +319,7 @@ public function renderReturnsNullIfNameOfAssignedVariableIsNotEqualToValue()
$this->view->assign('foo', $value);
$expectedResult = 'null';
- $actualResult = $this->view->render();
+ $actualResult = $this->view->render()->getBody()->getContents();
self::assertEquals($expectedResult, $actualResult);
}
@@ -333,7 +333,7 @@ public function renderOnlyRendersVariableWithTheNameValue()
->assign('someOtherVariable', 'Foo');
$expectedResult = '"Value"';
- $actualResult = $this->view->render();
+ $actualResult = $this->view->render()->getBody()->getContents();
self::assertEquals($expectedResult, $actualResult);
}
@@ -347,7 +347,7 @@ public function setVariablesToRenderOverridesValueToRender()
$this->view->setVariablesToRender(['foo']);
$expectedResult = '"Foo"';
- $actualResult = $this->view->render();
+ $actualResult = $this->view->render()->getBody()->getContents();
self::assertEquals($expectedResult, $actualResult);
}
@@ -363,7 +363,7 @@ public function renderRendersMultipleValuesIfTheyAreSpecifiedAsVariablesToRender
$this->view->setVariablesToRender(['value', 'secondValue']);
$expectedResult = '{"value":"Value1","secondValue":"Value2"}';
- $actualResult = $this->view->render();
+ $actualResult = $this->view->render()->getBody()->getContents();
self::assertEquals($expectedResult, $actualResult);
}
@@ -383,7 +383,7 @@ public function renderCanRenderMultipleComplexObjects()
$this->view->setVariablesToRender(['array', 'object']);
$expectedResult = '{"array":{"foo":{"bar":"Baz"}},"object":{"foo":"Foo"}}';
- $actualResult = $this->view->render();
+ $actualResult = $this->view->render()->getBody()->getContents();
self::assertEquals($expectedResult, $actualResult);
}
@@ -404,7 +404,7 @@ public function renderCanRenderPlainArray()
]);
$expectedResult = '[{"name":"Foo"},{"name":"Bar"}]';
- $actualResult = $this->view->render();
+ $actualResult = $this->view->render()->getBody()->getContents();
self::assertEquals($expectedResult, $actualResult);
}
@@ -425,7 +425,7 @@ public function descendAllKeepsArrayIndexes()
]);
$expectedResult = '[{"name":"Foo","secret":true},{"name":"Bar","secret":true}]';
- $actualResult = $this->view->render();
+ $actualResult = $this->view->render()->getBody()->getContents();
self::assertEquals($expectedResult, $actualResult);
}
@@ -445,7 +445,7 @@ public function renderTransformsJsonSerializableValues()
]);
$expectedResult = '{"name":"Foo"}';
- $actualResult = $this->view->render();
+ $actualResult = $this->view->render()->getBody()->getContents();
self::assertEquals($expectedResult, $actualResult);
}
@@ -462,7 +462,7 @@ public function viewAcceptsJsonEncodingOptions()
$expectedResult = json_encode($array, JSON_PRETTY_PRINT);
- $actualResult = $this->view->render();
+ $actualResult = $this->view->render()->getBody()->getContents();
self::assertEquals($expectedResult, $actualResult);
$unexpectedResult = json_encode($array);
@@ -482,7 +482,7 @@ public function viewObeysDateTimeFormatOption()
$expectedResult = json_encode(['foo' => '2021-05-02 13:00:00 GMT+0000']);
- $actualResult = $this->view->render();
+ $actualResult = $this->view->render()->getBody()->getContents();
$this->assertEquals($expectedResult, $actualResult);
}
}
From 227670a02cd8e11cd556ad991ce1d7d5d2ace515 Mon Sep 17 00:00:00 2001
From: mhsdesign <85400359+mhsdesign@users.noreply.github.com>
Date: Mon, 29 Jan 2024 16:29:41 +0100
Subject: [PATCH 05/10] Fix ci
---
.../Classes/Error/AbstractExceptionHandler.php | 14 ++++++++------
Neos.Flow/Classes/Mvc/View/AbstractView.php | 3 ---
2 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/Neos.Flow/Classes/Error/AbstractExceptionHandler.php b/Neos.Flow/Classes/Error/AbstractExceptionHandler.php
index f86f95fb49..1e753eb284 100644
--- a/Neos.Flow/Classes/Error/AbstractExceptionHandler.php
+++ b/Neos.Flow/Classes/Error/AbstractExceptionHandler.php
@@ -162,12 +162,14 @@ protected function buildView(\Throwable $exception, array $renderingOptions): Vi
$request->setControllerPackageKey('Neos.Flow');
$uriBuilder = new UriBuilder();
$uriBuilder->setRequest($request);
- $view->setControllerContext(new ControllerContext(
- $request,
- new ActionResponse(),
- new Arguments([]),
- $uriBuilder
- ));
+ if (method_exists($view, 'setControllerContext')) {
+ $view->setControllerContext(new ControllerContext(
+ $request,
+ new ActionResponse(),
+ new Arguments([]),
+ $uriBuilder
+ ));
+ }
if (isset($renderingOptions['variables'])) {
$view->assignMultiple($renderingOptions['variables']);
diff --git a/Neos.Flow/Classes/Mvc/View/AbstractView.php b/Neos.Flow/Classes/Mvc/View/AbstractView.php
index 08e2d9583b..8646a05140 100644
--- a/Neos.Flow/Classes/Mvc/View/AbstractView.php
+++ b/Neos.Flow/Classes/Mvc/View/AbstractView.php
@@ -11,11 +11,8 @@
* source code.
*/
-use Neos\Flow\Mvc\ActionResponse;
-use Neos\Flow\Mvc\Controller\Arguments;
use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\Mvc\Exception;
-use Neos\Flow\Mvc\Routing\UriBuilder;
/**
* An abstract View
From 1ae08a8c0b33685a93298f48724449c6c78b1845 Mon Sep 17 00:00:00 2001
From: mhsdesign <85400359+mhsdesign@users.noreply.github.com>
Date: Mon, 26 Feb 2024 12:48:58 +0100
Subject: [PATCH 06/10] !!! TASK: `ViewInterface::canRender`
Initially `canRender` was introduced to allow special logic in a controller to determine the correct view out of multiple options.
https://github.com/neos/flow-development-collection/commit/cb8fa242150ccdd8d5f9e7121c70c896efaaa62b
But with https://github.com/neos/flow-development-collection/commit/d69bcfe290a04b4079c8a764bb8a2c97b72d13d8 the canRender() checks were removed and leaves the method with zero usages and no _real_ implementation.
---
Neos.Flow/Classes/Mvc/View/AbstractView.php | 14 --------------
Neos.Flow/Classes/Mvc/View/ViewInterface.php | 8 --------
.../ViewsConfiguration/Fixtures/TemplateView.php | 12 ------------
.../Classes/View/AbstractTemplateView.php | 9 ---------
.../Tests/Unit/View/AbstractTemplateViewTest.php | 2 +-
5 files changed, 1 insertion(+), 44 deletions(-)
diff --git a/Neos.Flow/Classes/Mvc/View/AbstractView.php b/Neos.Flow/Classes/Mvc/View/AbstractView.php
index 8646a05140..b352885ee3 100644
--- a/Neos.Flow/Classes/Mvc/View/AbstractView.php
+++ b/Neos.Flow/Classes/Mvc/View/AbstractView.php
@@ -179,18 +179,4 @@ public function setControllerContext(ControllerContext $controllerContext)
{
$this->controllerContext = $controllerContext;
}
-
- /**
- * Tells if the view implementation can render the view for the given context.
- *
- * By default we assume that the view implementation can handle all kinds of
- * contexts. Override this method if that is not the case.
- *
- * @param ControllerContext $controllerContext
- * @return boolean true if the view has something useful to display, otherwise false
- */
- public function canRender(ControllerContext $controllerContext)
- {
- return true;
- }
}
diff --git a/Neos.Flow/Classes/Mvc/View/ViewInterface.php b/Neos.Flow/Classes/Mvc/View/ViewInterface.php
index 8a1422bc83..a0b0fef6d6 100644
--- a/Neos.Flow/Classes/Mvc/View/ViewInterface.php
+++ b/Neos.Flow/Classes/Mvc/View/ViewInterface.php
@@ -53,14 +53,6 @@ public function assign($key, $value);
*/
public function assignMultiple(array $values);
- /**
- * Tells if the view implementation can render the view for the given context.
- *
- * @param ControllerContext $controllerContext
- * @return boolean true if the view has something useful to display, otherwise false
- */
- public function canRender(ControllerContext $controllerContext);
-
/**
* Renders the view
*
diff --git a/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php b/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php
index 73a7cc2bc5..556b40ee3c 100644
--- a/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php
+++ b/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php
@@ -64,18 +64,6 @@ public function assignMultiple(array $values)
return $this;
}
- /**
- * This view can be used in any case.
- *
- * @param ControllerContext $controllerContext
- * @return boolean true
- * @api
- */
- public function canRender(ControllerContext $controllerContext)
- {
- return true;
- }
-
/**
* Renders the empty view
*
diff --git a/Neos.FluidAdaptor/Classes/View/AbstractTemplateView.php b/Neos.FluidAdaptor/Classes/View/AbstractTemplateView.php
index 7c2a3219eb..74074c2d07 100644
--- a/Neos.FluidAdaptor/Classes/View/AbstractTemplateView.php
+++ b/Neos.FluidAdaptor/Classes/View/AbstractTemplateView.php
@@ -177,15 +177,6 @@ public function setControllerContext(ControllerContext $controllerContext)
$this->baseRenderingContext->setControllerAction($request->getControllerActionName());
}
- /**
- * @param ControllerContext $controllerContext
- * @return boolean
- */
- public function canRender(ControllerContext $controllerContext)
- {
- return true;
- }
-
/**
* Renders a given section.
*
diff --git a/Neos.FluidAdaptor/Tests/Unit/View/AbstractTemplateViewTest.php b/Neos.FluidAdaptor/Tests/Unit/View/AbstractTemplateViewTest.php
index 3e6e66715b..5e61f82073 100644
--- a/Neos.FluidAdaptor/Tests/Unit/View/AbstractTemplateViewTest.php
+++ b/Neos.FluidAdaptor/Tests/Unit/View/AbstractTemplateViewTest.php
@@ -52,7 +52,7 @@ protected function setUp(): void
$this->renderingContext = $this->getMockBuilder(RenderingContext::class)->setMethods(['getViewHelperVariableContainer', 'getVariableProvider'])->disableOriginalConstructor()->getMock();
$this->renderingContext->expects(self::any())->method('getViewHelperVariableContainer')->will(self::returnValue($this->viewHelperVariableContainer));
$this->renderingContext->expects(self::any())->method('getVariableProvider')->will(self::returnValue($this->templateVariableContainer));
- $this->view = $this->getMockBuilder(AbstractTemplateView::class)->setMethods(['getTemplateSource', 'getLayoutSource', 'getPartialSource', 'canRender', 'getTemplateIdentifier', 'getLayoutIdentifier', 'getPartialIdentifier'])->getMock();
+ $this->view = $this->getMockBuilder(AbstractTemplateView::class)->setMethods(['getTemplateSource', 'getLayoutSource', 'getPartialSource', 'getTemplateIdentifier', 'getLayoutIdentifier', 'getPartialIdentifier'])->getMock();
$this->view->setRenderingContext($this->renderingContext);
}
From cc8f3a932ca34a9320e8d069f407cb3a742f9afa Mon Sep 17 00:00:00 2001
From: mhsdesign <85400359+mhsdesign@users.noreply.github.com>
Date: Fri, 15 Mar 2024 10:58:26 +0100
Subject: [PATCH 07/10] !!! TASK: Strict render type for views
`ResponseInterface|StreamInterface`
Please adjust your view to return a `StreamInterface` instead of a stream.
If you need a string at call site, please use `->getContent()` instead.
---
.../Classes/Error/DebugExceptionHandler.php | 17 +++++++++-
.../Error/ProductionExceptionHandler.php | 17 +++++++++-
.../Mvc/Controller/ActionController.php | 12 -------
Neos.Flow/Classes/Mvc/View/JsonView.php | 10 +++---
.../Classes/Mvc/View/SimpleTemplateView.php | 11 +++++--
Neos.Flow/Classes/Mvc/View/ViewInterface.php | 3 +-
.../Fixtures/TemplateView.php | 8 +++--
.../Classes/View/AbstractTemplateView.php | 12 +++++++
.../Controller/View/CustomView.php | 3 +-
.../Functional/View/StandaloneViewTest.php | 32 +++++++++----------
.../Classes/Service/GeneratorService.php | 2 +-
11 files changed, 84 insertions(+), 43 deletions(-)
diff --git a/Neos.Flow/Classes/Error/DebugExceptionHandler.php b/Neos.Flow/Classes/Error/DebugExceptionHandler.php
index f64b7b4f0a..d3f490a2ca 100644
--- a/Neos.Flow/Classes/Error/DebugExceptionHandler.php
+++ b/Neos.Flow/Classes/Error/DebugExceptionHandler.php
@@ -15,6 +15,7 @@
use Neos\Flow\Core\Bootstrap;
use Neos\Flow\Http\Helper\ResponseInformationHelper;
use Neos\Flow\ObjectManagement\ObjectManagerInterface;
+use Psr\Http\Message\ResponseInterface;
/**
* A basic but solid exception handler which catches everything which
@@ -76,7 +77,21 @@ protected function echoExceptionWeb($exception)
}
try {
- echo $this->buildView($exception, $this->renderingOptions)->render();
+ $stream = $this->buildView($exception, $this->renderingOptions)->render();
+ if ($stream instanceof ResponseInterface) {
+ /**
+ * The http status code will already be sent, and we are only currently interested in the content stream
+ * Thus, we unwrap the repose here:
+ */
+ $stream = $stream->getBody();
+ }
+ $resourceOrString = $stream->detach() ?: $stream->getContents();
+ if (is_resource($resourceOrString)) {
+ fpassthru($resourceOrString);
+ fclose($resourceOrString);
+ } else {
+ echo $resourceOrString;
+ }
} catch (\Throwable $throwable) {
$this->renderStatically($statusCode, $throwable);
}
diff --git a/Neos.Flow/Classes/Error/ProductionExceptionHandler.php b/Neos.Flow/Classes/Error/ProductionExceptionHandler.php
index 9a588de1e1..5337e70c3f 100644
--- a/Neos.Flow/Classes/Error/ProductionExceptionHandler.php
+++ b/Neos.Flow/Classes/Error/ProductionExceptionHandler.php
@@ -13,6 +13,7 @@
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Http\Helper\ResponseInformationHelper;
+use Psr\Http\Message\ResponseInterface;
/**
* A quite exception handler which catches but ignores any exception.
@@ -39,7 +40,21 @@ protected function echoExceptionWeb($exception)
try {
if ($this->useCustomErrorView()) {
try {
- echo $this->buildView($exception, $this->renderingOptions)->render();
+ $stream = $this->buildView($exception, $this->renderingOptions)->render();
+ if ($stream instanceof ResponseInterface) {
+ /**
+ * The http status code will already be sent, and we are only currently interested in the content stream
+ * Thus, we unwrap the repose here:
+ */
+ $stream = $stream->getBody();
+ }
+ $resourceOrString = $stream->detach() ?: $stream->getContents();
+ if (is_resource($resourceOrString)) {
+ fpassthru($resourceOrString);
+ fclose($resourceOrString);
+ } else {
+ echo $resourceOrString;
+ }
} catch (\Throwable $throwable) {
$this->renderStatically($statusCode, $throwable);
}
diff --git a/Neos.Flow/Classes/Mvc/Controller/ActionController.php b/Neos.Flow/Classes/Mvc/Controller/ActionController.php
index fa50a65f61..0e4323a325 100644
--- a/Neos.Flow/Classes/Mvc/Controller/ActionController.php
+++ b/Neos.Flow/Classes/Mvc/Controller/ActionController.php
@@ -823,14 +823,6 @@ protected function renderView()
{
$result = $this->view->render();
- if (is_string($result)) {
- $this->response->setContent($result);
- }
-
- if ($result instanceof ActionResponse) {
- $result->mergeIntoParentResponse($this->response);
- }
-
if ($result instanceof ResponseInterface) {
$this->response->replaceHttpResponse($result);
if ($result->hasHeader('Content-Type')) {
@@ -838,10 +830,6 @@ protected function renderView()
}
}
- if (is_object($result) && is_callable([$result, '__toString'])) {
- $this->response->setContent((string)$result);
- }
-
if ($result instanceof StreamInterface) {
$this->response->setContent($result);
}
diff --git a/Neos.Flow/Classes/Mvc/View/JsonView.php b/Neos.Flow/Classes/Mvc/View/JsonView.php
index 1dd8f8b6cd..6935c669cf 100644
--- a/Neos.Flow/Classes/Mvc/View/JsonView.php
+++ b/Neos.Flow/Classes/Mvc/View/JsonView.php
@@ -13,9 +13,9 @@
*/
use GuzzleHttp\Psr7\Response;
-use GuzzleHttp\Psr7\Utils;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Persistence\PersistenceManagerInterface;
+use Neos\Http\Factories\StreamFactoryTrait;
use Neos\Utility\ObjectAccess;
use Neos\Utility\TypeHandling;
use Psr\Http\Message\ResponseInterface;
@@ -23,10 +23,12 @@
/**
* A JSON view
*
- * @api
+ * @deprecated please use json_encode instead
*/
class JsonView extends AbstractView
{
+ use StreamFactoryTrait;
+
/**
* Supported options
* @var array
@@ -195,14 +197,14 @@ public function setConfiguration(array $configuration)
* @return ResponseInterface The JSON encoded variables
* @api
*/
- public function render()
+ public function render(): ResponseInterface
{
$response = new Response();
$response = $response->withHeader('Content-Type', 'application/json');
$propertiesToRender = $this->renderArray();
$options = $this->getOption('jsonEncodingOptions');
$value = json_encode($propertiesToRender, JSON_THROW_ON_ERROR | $options);
- return $response->withBody(Utils::streamFor($value));
+ return $response->withBody($this->createStream($value));
}
/**
diff --git a/Neos.Flow/Classes/Mvc/View/SimpleTemplateView.php b/Neos.Flow/Classes/Mvc/View/SimpleTemplateView.php
index 1bbe0c104f..5454cea8e8 100644
--- a/Neos.Flow/Classes/Mvc/View/SimpleTemplateView.php
+++ b/Neos.Flow/Classes/Mvc/View/SimpleTemplateView.php
@@ -11,7 +11,9 @@
* source code.
*/
+use Neos\Http\Factories\StreamFactoryTrait;
use Neos\Utility\ObjectAccess;
+use Psr\Http\Message\StreamInterface;
/**
* An abstract View
@@ -20,6 +22,8 @@
*/
class SimpleTemplateView extends AbstractView
{
+ use StreamFactoryTrait;
+
/**
* @var array
*/
@@ -31,10 +35,9 @@ class SimpleTemplateView extends AbstractView
/**
* Renders the view
*
- * @return string The rendered view
* @api
*/
- public function render()
+ public function render(): StreamInterface
{
$source = $this->getOption('templateSource');
$templatePathAndFilename = $this->getOption('templatePathAndFilename');
@@ -42,8 +45,10 @@ public function render()
$source = file_get_contents($templatePathAndFilename);
}
- return preg_replace_callback('/\{([a-zA-Z0-9\-_.]+)\}/', function ($matches) {
+ $content = preg_replace_callback('/\{([a-zA-Z0-9\-_.]+)\}/', function ($matches) {
return ObjectAccess::getPropertyPath($this->variables, $matches[1]);
}, $source);
+
+ return $this->createStream($content);
}
}
diff --git a/Neos.Flow/Classes/Mvc/View/ViewInterface.php b/Neos.Flow/Classes/Mvc/View/ViewInterface.php
index a0b0fef6d6..ec5cbf8c2b 100644
--- a/Neos.Flow/Classes/Mvc/View/ViewInterface.php
+++ b/Neos.Flow/Classes/Mvc/View/ViewInterface.php
@@ -56,10 +56,9 @@ public function assignMultiple(array $values);
/**
* Renders the view
*
- * @return string|ActionResponse|ResponseInterface|StreamInterface|object The rendered result; object is only handled if __toString() exists!
* @api
*/
- public function render();
+ public function render(): ResponseInterface|StreamInterface;
/**
* Factory method to create an instance with given options.
diff --git a/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php b/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php
index 556b40ee3c..7d28fda985 100644
--- a/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php
+++ b/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php
@@ -12,6 +12,8 @@
*/
use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\Mvc\View\AbstractView;
+use Neos\Http\Factories\StreamFactoryTrait;
+use Psr\Http\Message\StreamInterface;
/**
* An empty view - a special case.
@@ -19,6 +21,8 @@
*/
final class TemplateView extends AbstractView
{
+ use StreamFactoryTrait;
+
/**
* @var array
*/
@@ -69,9 +73,9 @@ public function assignMultiple(array $values)
*
* @return string An empty string
*/
- public function render()
+ public function render(): StreamInterface
{
- return get_class($this);
+ return $this->createStream(get_class($this));
}
/**
diff --git a/Neos.FluidAdaptor/Classes/View/AbstractTemplateView.php b/Neos.FluidAdaptor/Classes/View/AbstractTemplateView.php
index 74074c2d07..c9433cb6cf 100644
--- a/Neos.FluidAdaptor/Classes/View/AbstractTemplateView.php
+++ b/Neos.FluidAdaptor/Classes/View/AbstractTemplateView.php
@@ -16,6 +16,8 @@
use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\Mvc\View\ViewInterface;
use Neos\FluidAdaptor\Core\Rendering\RenderingContext;
+use Neos\Http\Factories\StreamFactoryTrait;
+use Psr\Http\Message\StreamInterface;
/**
* The abstract base of all Fluid views.
@@ -23,6 +25,8 @@
*/
abstract class AbstractTemplateView extends \TYPO3Fluid\Fluid\View\AbstractTemplateView implements ViewInterface
{
+ use StreamFactoryTrait;
+
/**
* This contains the supported options, their default values, descriptions and types.
* Syntax example:
@@ -106,6 +110,14 @@ abstract class AbstractTemplateView extends \TYPO3Fluid\Fluid\View\AbstractTempl
*/
protected $controllerContext;
+ /**
+ * @phpstan-ignore-next-line we are incompatible with the fluid view and should use composition instead
+ */
+ public function render($actionName = null): StreamInterface
+ {
+ return $this->createStream(parent::render($actionName));
+ }
+
/**
* Factory method to create an instance with given options.
*
diff --git a/Neos.FluidAdaptor/Tests/Functional/Core/Fixtures/ViewHelpers/Controller/View/CustomView.php b/Neos.FluidAdaptor/Tests/Functional/Core/Fixtures/ViewHelpers/Controller/View/CustomView.php
index 9ec175bb04..a963e9aee6 100644
--- a/Neos.FluidAdaptor/Tests/Functional/Core/Fixtures/ViewHelpers/Controller/View/CustomView.php
+++ b/Neos.FluidAdaptor/Tests/Functional/Core/Fixtures/ViewHelpers/Controller/View/CustomView.php
@@ -13,10 +13,11 @@
use GuzzleHttp\Psr7\Response;
use Neos\Flow\Mvc\View\AbstractView;
+use Psr\Http\Message\ResponseInterface;
class CustomView extends AbstractView
{
- public function render()
+ public function render(): ResponseInterface
{
return new Response(418, ['X-Flow-Special-Header' => 'YEAH!'], 'Hello World!');
}
diff --git a/Neos.FluidAdaptor/Tests/Functional/View/StandaloneViewTest.php b/Neos.FluidAdaptor/Tests/Functional/View/StandaloneViewTest.php
index 7f3a7fb781..ff4e32dace 100644
--- a/Neos.FluidAdaptor/Tests/Functional/View/StandaloneViewTest.php
+++ b/Neos.FluidAdaptor/Tests/Functional/View/StandaloneViewTest.php
@@ -58,7 +58,7 @@ public function inlineTemplateIsEvaluatedCorrectly(): void
$standaloneView->setTemplateSource('This is my cool {foo} template!');
$expected = 'This is my cool bar template!';
- $actual = $standaloneView->render();
+ $actual = $standaloneView->render()->getContents();
self::assertSame($expected, $actual);
}
@@ -89,7 +89,7 @@ public function renderThrowsExceptionIfNeitherTemplateSourceNorTemplatePathAndFi
$actionRequest = ActionRequest::fromHttpRequest($httpRequest);
$standaloneView = new StandaloneView($actionRequest, $this->standaloneViewNonce);
- $standaloneView->render();
+ $standaloneView->render()->getContents();
}
/**
@@ -103,7 +103,7 @@ public function renderThrowsExceptionSpecifiedTemplatePathAndFilenameDoesNotExis
$standaloneView = new StandaloneView($actionRequest, $this->standaloneViewNonce);
$standaloneView->setTemplatePathAndFilename(__DIR__ . '/Fixtures/NonExistingTemplate.txt');
- $standaloneView->render();
+ $standaloneView->render()->getContents();
}
/**
@@ -117,7 +117,7 @@ public function renderThrowsExceptionIfWrongEnctypeIsSetForFormUpload(): void
$standaloneView = new StandaloneView($actionRequest, $this->standaloneViewNonce);
$standaloneView->setTemplatePathAndFilename(__DIR__ . '/Fixtures/TestTemplateWithFormUpload.txt');
- $standaloneView->render();
+ $standaloneView->render()->getContents();
}
/**
@@ -131,7 +131,7 @@ public function renderThrowsExceptionIfSpecifiedTemplatePathAndFilenamePointsToA
$standaloneView = new StandaloneView($actionRequest, $this->standaloneViewNonce);
$standaloneView->setTemplatePathAndFilename(__DIR__ . '/Fixtures');
- $standaloneView->render();
+ $standaloneView->render()->getContents();
}
/**
@@ -148,7 +148,7 @@ public function templatePathAndFilenameIsLoaded(): void
$standaloneView->setTemplatePathAndFilename(__DIR__ . '/Fixtures/TestTemplate.txt');
$expected = 'This is a test template. Hello Robert.';
- $actual = $standaloneView->render();
+ $actual = $standaloneView->render()->getContents();
self::assertSame($expected, $actual);
}
@@ -162,7 +162,7 @@ public function variablesAreEscapedByDefault(): void
$standaloneView->setTemplateSource('Hello {name}.');
$expected = 'Hello Sebastian <script>alert("dangerous");</script>.';
- $actual = $standaloneView->render();
+ $actual = $standaloneView->render()->getContents();
self::assertSame($expected, $actual);
}
@@ -176,7 +176,7 @@ public function variablesAreNotEscapedIfEscapingIsDisabled(): void
$standaloneView->setTemplateSource('{escapingEnabled=false}Hello {name}.');
$expected = 'Hello Sebastian .';
- $actual = $standaloneView->render();
+ $actual = $standaloneView->render()->getContents();
self::assertSame($expected, $actual);
}
@@ -192,7 +192,7 @@ public function variablesCanBeNested()
$standaloneView->setTemplateSource('{config.{type}.value.{flavor}}');
$expected = 'Okayish';
- $actual = $standaloneView->render();
+ $actual = $standaloneView->render()->getContents();
$this->assertSame($expected, $actual);
}
@@ -209,7 +209,7 @@ public function partialWithDefaultLocationIsUsedIfNoPartialPathIsSetExplicitly()
$standaloneView->setTemplatePathAndFilename(__DIR__ . '/Fixtures/TestTemplateWithPartial.txt');
$expected = 'This is a test template. Hello Robert.';
- $actual = $standaloneView->render();
+ $actual = $standaloneView->render()->getContents();
self::assertSame($expected, $actual);
}
@@ -227,7 +227,7 @@ public function explicitPartialPathIsUsed(): void
$standaloneView->setPartialRootPath(__DIR__ . '/Fixtures/SpecialPartialsDirectory');
$expected = 'This is a test template. Hello Karsten.';
- $actual = $standaloneView->render();
+ $actual = $standaloneView->render()->getContents();
self::assertSame($expected, $actual);
}
@@ -244,7 +244,7 @@ public function layoutWithDefaultLocationIsUsedIfNoLayoutPathIsSetExplicitly():
$standaloneView->setTemplatePathAndFilename(__DIR__ . '/Fixtures/TestTemplateWithLayout.txt');
$expected = 'Hey HEY HO';
- $actual = $standaloneView->render();
+ $actual = $standaloneView->render()->getContents();
self::assertSame($expected, $actual);
}
@@ -261,7 +261,7 @@ public function explicitLayoutPathIsUsed(): void
$standaloneView->setLayoutRootPath(__DIR__ . '/Fixtures/SpecialLayouts');
$expected = 'Hey -- overridden -- HEY HO';
- $actual = $standaloneView->render();
+ $actual = $standaloneView->render()->getContents();
self::assertSame($expected, $actual);
}
@@ -278,7 +278,7 @@ public function viewThrowsExceptionWhenUnknownViewHelperIsCalled(): void
$standaloneView->setTemplatePathAndFilename(__DIR__ . '/Fixtures/TestTemplateWithUnknownViewHelper.txt');
$standaloneView->setLayoutRootPath(__DIR__ . '/Fixtures/SpecialLayouts');
- $standaloneView->render();
+ $standaloneView->render()->getContents();
}
/**
@@ -294,7 +294,7 @@ public function xmlNamespacesCanBeIgnored(): void
$standaloneView->setLayoutRootPath(__DIR__ . '/Fixtures/SpecialLayouts');
$expected = 'foobar';
- $actual = $standaloneView->render();
+ $actual = $standaloneView->render()->getContents();
self::assertSame($expected, $actual);
}
@@ -372,7 +372,7 @@ public function formViewHelpersOutsideOfFormWork(): void
$standaloneView->setTemplatePathAndFilename(__DIR__ . '/Fixtures/TestTemplateWithFormField.txt');
$expected = 'This is a test template.';
- $actual = $standaloneView->render();
+ $actual = $standaloneView->render()->getContents();
self::assertSame($expected, $actual);
}
}
diff --git a/Neos.Kickstarter/Classes/Service/GeneratorService.php b/Neos.Kickstarter/Classes/Service/GeneratorService.php
index 240b97cd95..e872b5f181 100644
--- a/Neos.Kickstarter/Classes/Service/GeneratorService.php
+++ b/Neos.Kickstarter/Classes/Service/GeneratorService.php
@@ -583,7 +583,7 @@ protected function renderTemplate($templatePathAndFilename, array $contextVariab
$standaloneView = new StandaloneView();
$standaloneView->setTemplatePathAndFilename($templatePathAndFilename);
$standaloneView->assignMultiple($contextVariables);
- return $standaloneView->render();
+ return $standaloneView->render()->getContents();
}
/**
From 9bb513355c60dc671751781730f0f4f4242c49f3 Mon Sep 17 00:00:00 2001
From: mhsdesign <85400359+mhsdesign@users.noreply.github.com>
Date: Fri, 15 Mar 2024 11:01:53 +0100
Subject: [PATCH 08/10] TASK: Refactor stream sending
---
Neos.Flow/Classes/Error/DebugExceptionHandler.php | 8 +-------
.../Classes/Error/ProductionExceptionHandler.php | 8 +-------
.../Http/Helper/ResponseInformationHelper.php | 12 ++++++++++++
Neos.Flow/Classes/Http/RequestHandler.php | 8 +-------
4 files changed, 15 insertions(+), 21 deletions(-)
diff --git a/Neos.Flow/Classes/Error/DebugExceptionHandler.php b/Neos.Flow/Classes/Error/DebugExceptionHandler.php
index d3f490a2ca..3718a0e289 100644
--- a/Neos.Flow/Classes/Error/DebugExceptionHandler.php
+++ b/Neos.Flow/Classes/Error/DebugExceptionHandler.php
@@ -85,13 +85,7 @@ protected function echoExceptionWeb($exception)
*/
$stream = $stream->getBody();
}
- $resourceOrString = $stream->detach() ?: $stream->getContents();
- if (is_resource($resourceOrString)) {
- fpassthru($resourceOrString);
- fclose($resourceOrString);
- } else {
- echo $resourceOrString;
- }
+ ResponseInformationHelper::sendStream($stream);
} catch (\Throwable $throwable) {
$this->renderStatically($statusCode, $throwable);
}
diff --git a/Neos.Flow/Classes/Error/ProductionExceptionHandler.php b/Neos.Flow/Classes/Error/ProductionExceptionHandler.php
index 5337e70c3f..a637f90e0e 100644
--- a/Neos.Flow/Classes/Error/ProductionExceptionHandler.php
+++ b/Neos.Flow/Classes/Error/ProductionExceptionHandler.php
@@ -48,13 +48,7 @@ protected function echoExceptionWeb($exception)
*/
$stream = $stream->getBody();
}
- $resourceOrString = $stream->detach() ?: $stream->getContents();
- if (is_resource($resourceOrString)) {
- fpassthru($resourceOrString);
- fclose($resourceOrString);
- } else {
- echo $resourceOrString;
- }
+ ResponseInformationHelper::sendStream($stream);
} catch (\Throwable $throwable) {
$this->renderStatically($statusCode, $throwable);
}
diff --git a/Neos.Flow/Classes/Http/Helper/ResponseInformationHelper.php b/Neos.Flow/Classes/Http/Helper/ResponseInformationHelper.php
index 1858a2b88e..f120eba029 100644
--- a/Neos.Flow/Classes/Http/Helper/ResponseInformationHelper.php
+++ b/Neos.Flow/Classes/Http/Helper/ResponseInformationHelper.php
@@ -18,6 +18,7 @@
use Neos\Flow\Http\CacheControlDirectives;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamInterface;
/**
* Helper to extract various information from PSR-7 responses.
@@ -242,4 +243,15 @@ public static function makeStandardsCompliant(ResponseInterface $response, Reque
return $response;
}
+
+ public static function sendStream(StreamInterface $stream): void
+ {
+ $body = $stream->detach() ?: $stream->getContents();
+ if (is_resource($body)) {
+ fpassthru($body);
+ fclose($body);
+ } else {
+ echo $body;
+ }
+ }
}
diff --git a/Neos.Flow/Classes/Http/RequestHandler.php b/Neos.Flow/Classes/Http/RequestHandler.php
index 2a65d6ad29..249780b287 100644
--- a/Neos.Flow/Classes/Http/RequestHandler.php
+++ b/Neos.Flow/Classes/Http/RequestHandler.php
@@ -171,12 +171,6 @@ protected function sendResponse(ResponseInterface $response)
ob_end_flush();
}
- $body = $response->getBody()->detach() ?: $response->getBody()->getContents();
- if (is_resource($body)) {
- fpassthru($body);
- fclose($body);
- } else {
- echo $body;
- }
+ ResponseInformationHelper::sendStream($response->getBody());
}
}
From ff51048ba4d90ccce01a6d5c7251fa3bcbca50ba Mon Sep 17 00:00:00 2001
From: mhsdesign <85400359+mhsdesign@users.noreply.github.com>
Date: Sat, 16 Mar 2024 20:32:15 +0100
Subject: [PATCH 09/10] TASK: Update composer.json build
---
Neos.Flow/Classes/Mvc/View/ViewInterface.php | 1 -
.../Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php | 1 -
2 files changed, 2 deletions(-)
diff --git a/Neos.Flow/Classes/Mvc/View/ViewInterface.php b/Neos.Flow/Classes/Mvc/View/ViewInterface.php
index ec5cbf8c2b..06b8399f08 100644
--- a/Neos.Flow/Classes/Mvc/View/ViewInterface.php
+++ b/Neos.Flow/Classes/Mvc/View/ViewInterface.php
@@ -11,7 +11,6 @@
* source code.
*/
-use Neos\Flow\Mvc\ActionResponse;
use Neos\Flow\Mvc\Controller\ControllerContext;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
diff --git a/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php b/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php
index 7d28fda985..4e8658ac2a 100644
--- a/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php
+++ b/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php
@@ -10,7 +10,6 @@
* information, please view the LICENSE file which was distributed with this
* source code.
*/
-use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\Mvc\View\AbstractView;
use Neos\Http\Factories\StreamFactoryTrait;
use Psr\Http\Message\StreamInterface;
From 524f61e19a23a13a2280b2f95bfec2f75025d281 Mon Sep 17 00:00:00 2001
From: mhsdesign <85400359+mhsdesign@users.noreply.github.com>
Date: Sun, 17 Mar 2024 22:35:58 +0100
Subject: [PATCH 10/10] !!! TASK: Add strict types to `ViewInterface`
---
Neos.Flow/Classes/Mvc/View/AbstractView.php | 12 +++++------
Neos.Flow/Classes/Mvc/View/ViewInterface.php | 20 +++++++++----------
.../Fixtures/TemplateView.php | 4 ++--
.../Classes/View/AbstractTemplateView.php | 16 +++++++++++++--
.../Classes/View/StandaloneView.php | 4 ++--
5 files changed, 34 insertions(+), 22 deletions(-)
diff --git a/Neos.Flow/Classes/Mvc/View/AbstractView.php b/Neos.Flow/Classes/Mvc/View/AbstractView.php
index b352885ee3..ea85dfbd00 100644
--- a/Neos.Flow/Classes/Mvc/View/AbstractView.php
+++ b/Neos.Flow/Classes/Mvc/View/AbstractView.php
@@ -61,9 +61,9 @@ abstract class AbstractView implements ViewInterface
* Factory method to create an instance with given options.
*
* @param array $options
- * @return ViewInterface
+ * @return static
*/
- public static function createWithOptions(array $options)
+ public static function createWithOptions(array $options): self
{
return new static($options);
}
@@ -143,10 +143,10 @@ public function setOption($optionName, $value)
*
* @param string $key Key of variable
* @param mixed $value Value of object
- * @return AbstractView an instance of $this, to enable chaining
+ * @return $this for chaining
* @api
*/
- public function assign($key, $value)
+ public function assign(string $key, mixed $value): self
{
$this->variables[$key] = $value;
return $this;
@@ -156,10 +156,10 @@ public function assign($key, $value)
* Add multiple variables to $this->variables.
*
* @param array $values array in the format array(key1 => value1, key2 => value2)
- * @return AbstractView an instance of $this, to enable chaining
+ * @return $this for chaining
* @api
*/
- public function assignMultiple(array $values)
+ public function assignMultiple(array $values): self
{
foreach ($values as $key => $value) {
$this->assign($key, $value);
diff --git a/Neos.Flow/Classes/Mvc/View/ViewInterface.php b/Neos.Flow/Classes/Mvc/View/ViewInterface.php
index 06b8399f08..838b401ace 100644
--- a/Neos.Flow/Classes/Mvc/View/ViewInterface.php
+++ b/Neos.Flow/Classes/Mvc/View/ViewInterface.php
@@ -30,27 +30,27 @@ interface ViewInterface
* @param ControllerContext $controllerContext Context of the controller associated with this view
* @return void
*/
- // public function setControllerContext(ControllerContext $controllerContext);
+ // public function setControllerContext(ControllerContext $controllerContext): void;
/**
* Add a variable to the view data collection.
- * Can be chained, so $this->view->assign(..., ...)->assign(..., ...); is possible
+ * Can be chained: $this->view->assign(..., ...)->assign(..., ...);
*
* @param string $key Key of variable
* @param mixed $value Value of object
- * @return ViewInterface an instance of $this, to enable chaining
+ * @return $this for chaining
* @api
*/
- public function assign($key, $value);
+ public function assign(string $key, mixed $value): self;
/**
* Add multiple variables to the view data collection
*
- * @param array $values array in the format array(key1 => value1, key2 => value2)
- * @return ViewInterface an instance of $this, to enable chaining
+ * @param array $values associative array with the key being its name
+ * @return $this for chaining
* @api
*/
- public function assignMultiple(array $values);
+ public function assignMultiple(array $values): self;
/**
* Renders the view
@@ -62,8 +62,8 @@ public function render(): ResponseInterface|StreamInterface;
/**
* Factory method to create an instance with given options.
*
- * @param array $options
- * @return ViewInterface
+ * @param array $options
+ * @return static
*/
- public static function createWithOptions(array $options);
+ public static function createWithOptions(array $options): self;
}
diff --git a/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php b/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php
index 4e8658ac2a..a5c26b44c1 100644
--- a/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php
+++ b/Neos.Flow/Tests/Functional/Mvc/ViewsConfiguration/Fixtures/TemplateView.php
@@ -50,7 +50,7 @@ final class TemplateView extends AbstractView
* @return self instance of $this to allow chaining
* @api
*/
- public function assign($key, $value)
+ public function assign(string $key, mixed $value): self
{
return $this;
}
@@ -62,7 +62,7 @@ public function assign($key, $value)
* @return self instance of $this to allow chaining
* @api
*/
- public function assignMultiple(array $values)
+ public function assignMultiple(array $values): self
{
return $this;
}
diff --git a/Neos.FluidAdaptor/Classes/View/AbstractTemplateView.php b/Neos.FluidAdaptor/Classes/View/AbstractTemplateView.php
index c9433cb6cf..bb92d96905 100644
--- a/Neos.FluidAdaptor/Classes/View/AbstractTemplateView.php
+++ b/Neos.FluidAdaptor/Classes/View/AbstractTemplateView.php
@@ -118,13 +118,25 @@ public function render($actionName = null): StreamInterface
return $this->createStream(parent::render($actionName));
}
+ public function assign($key, $value): self
+ {
+ // layer to fix incompatibility error with typo3 fluid interface
+ return parent::assign($key, $value);
+ }
+
+ public function assignMultiple(array $values): self
+ {
+ // layer to fix incompatibility error with typo3 fluid interface
+ return parent::assignMultiple($values);
+ }
+
/**
* Factory method to create an instance with given options.
*
* @param array $options
- * @return AbstractTemplateView
+ * @return static
*/
- public static function createWithOptions(array $options)
+ public static function createWithOptions(array $options): self
{
return new static($options);
}
diff --git a/Neos.FluidAdaptor/Classes/View/StandaloneView.php b/Neos.FluidAdaptor/Classes/View/StandaloneView.php
index e738464e68..34252beb07 100644
--- a/Neos.FluidAdaptor/Classes/View/StandaloneView.php
+++ b/Neos.FluidAdaptor/Classes/View/StandaloneView.php
@@ -65,9 +65,9 @@ class StandaloneView extends AbstractTemplateView
* Factory method to create an instance with given options.
*
* @param array $options
- * @return StandaloneView
+ * @return static
*/
- public static function createWithOptions(array $options)
+ public static function createWithOptions(array $options): self
{
return new static(null, $options);
}