Skip to content

Commit

Permalink
Merge pull request #3286 from mhsdesign/feature/viewInterfaceWithoutC…
Browse files Browse the repository at this point in the history
…ontrollerContext

!!! FEATURE: `ViewInterface` returns PSR `StreamInterface`
  • Loading branch information
mhsdesign authored Apr 24, 2024
2 parents 9d7a108 + 524f61e commit 35386a2
Show file tree
Hide file tree
Showing 19 changed files with 162 additions and 194 deletions.
14 changes: 8 additions & 6 deletions Neos.Flow/Classes/Error/AbstractExceptionHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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']);
Expand Down
11 changes: 10 additions & 1 deletion Neos.Flow/Classes/Error/DebugExceptionHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -76,7 +77,15 @@ 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();
}
ResponseInformationHelper::sendStream($stream);
} catch (\Throwable $throwable) {
$this->renderStatically($statusCode, $throwable);
}
Expand Down
11 changes: 10 additions & 1 deletion Neos.Flow/Classes/Error/ProductionExceptionHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -39,7 +40,15 @@ 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();
}
ResponseInformationHelper::sendStream($stream);
} catch (\Throwable $throwable) {
$this->renderStatically($statusCode, $throwable);
}
Expand Down
12 changes: 12 additions & 0 deletions Neos.Flow/Classes/Http/Helper/ResponseInformationHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}
}
}
8 changes: 1 addition & 7 deletions Neos.Flow/Classes/Http/RequestHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
17 changes: 4 additions & 13 deletions Neos.Flow/Classes/Mvc/Controller/ActionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -820,25 +823,13 @@ 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')) {
$this->response->setContentType($result->getHeaderLine('Content-Type'));
}
}

if (is_object($result) && is_callable([$result, '__toString'])) {
$this->response->setContent((string)$result);
}

if ($result instanceof StreamInterface) {
$this->response->setContent($result);
}
Expand Down
33 changes: 11 additions & 22 deletions Neos.Flow/Classes/Mvc/View/AbstractView.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,18 @@ 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;

/**
* 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);
}
Expand Down Expand Up @@ -141,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;
Expand All @@ -154,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);
Expand All @@ -168,26 +170,13 @@ 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)
{
$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;
}
}
23 changes: 12 additions & 11 deletions Neos.Flow/Classes/Mvc/View/JsonView.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,23 @@
* source code.
*/

use GuzzleHttp\Psr7\Response;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\Persistence\PersistenceManagerInterface;
use Neos\Http\Factories\StreamFactoryTrait;
use Neos\Utility\ObjectAccess;
use Neos\Utility\TypeHandling;
use Psr\Http\Message\ResponseInterface;

/**
* A JSON view
*
* @api
* @deprecated please use json_encode instead
*/
class JsonView extends AbstractView
{
use StreamFactoryTrait;

/**
* Supported options
* @var array
Expand All @@ -50,11 +54,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
*
Expand Down Expand Up @@ -195,15 +194,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()
public function render(): ResponseInterface
{
$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($this->createStream($value));
}

/**
Expand Down
11 changes: 8 additions & 3 deletions Neos.Flow/Classes/Mvc/View/SimpleTemplateView.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
* source code.
*/

use Neos\Http\Factories\StreamFactoryTrait;
use Neos\Utility\ObjectAccess;
use Psr\Http\Message\StreamInterface;

/**
* An abstract View
Expand All @@ -20,6 +22,8 @@
*/
class SimpleTemplateView extends AbstractView
{
use StreamFactoryTrait;

/**
* @var array
*/
Expand All @@ -31,19 +35,20 @@ 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');
if ($templatePathAndFilename !== null) {
$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);
}
}
Loading

0 comments on commit 35386a2

Please sign in to comment.