minor #12147 [TwigBundle] Fix error page preview for custom twig.exception_controller (mpdude)
This PR was submitted for the master branch but it was merged into the 2.6 branch instead (closes #12147).
Discussion
----------
[TwigBundle] Fix error page preview for custom twig.exception_controller
| Q | A
| ------------- | ---
| Bug fix? | yes
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | #12147
| License | MIT
| Doc PR | n/a
The `testErrorPageAction()` always used the `showAction()` of TwigBundle's `ExceptionController`.
You can, however, configure an alternate controller by setting `twig.exception_controller`.
Thus, in order to get a proper preview, we need to forward to this configured controller (which
may be the default one).
This requires us to pass an additional parameter to `ExceptionController::showAction` to be able to
get the *error* page even if configured otherwise in the constructor.
(The other approach would have been to fiddle around with `ExceptionController`'s `debug` flag through a setter when going through the preview action, but that would have been even more messy.)
Commits
-------
2065e00
[TwigBundle] Fix error page preview for custom twig.exception_controller
This commit is contained in:
commit
c7b899c8aa
@ -19,7 +19,8 @@ use Symfony\Component\HttpFoundation\Response;
|
|||||||
use Symfony\Component\Templating\TemplateReferenceInterface;
|
use Symfony\Component\Templating\TemplateReferenceInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ExceptionController.
|
* ExceptionController renders error or exception pages for a given
|
||||||
|
* FlattenException.
|
||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
* @author Matthias Pigulla <mp@webfactory.de>
|
* @author Matthias Pigulla <mp@webfactory.de>
|
||||||
@ -27,6 +28,10 @@ use Symfony\Component\Templating\TemplateReferenceInterface;
|
|||||||
class ExceptionController
|
class ExceptionController
|
||||||
{
|
{
|
||||||
protected $twig;
|
protected $twig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool Show error (false) or exception (true) pages by default.
|
||||||
|
*/
|
||||||
protected $debug;
|
protected $debug;
|
||||||
|
|
||||||
public function __construct(\Twig_Environment $twig, $debug)
|
public function __construct(\Twig_Environment $twig, $debug)
|
||||||
@ -38,6 +43,10 @@ class ExceptionController
|
|||||||
/**
|
/**
|
||||||
* Converts an Exception to a Response.
|
* Converts an Exception to a Response.
|
||||||
*
|
*
|
||||||
|
* A "showException" request parameter can be used to force display of an error page (when set to false) or
|
||||||
|
* the exception page (when true). If it is not present, the "debug" value passed into the constructor will
|
||||||
|
* be used.
|
||||||
|
*
|
||||||
* @param Request $request The request
|
* @param Request $request The request
|
||||||
* @param FlattenException $exception A FlattenException instance
|
* @param FlattenException $exception A FlattenException instance
|
||||||
* @param DebugLoggerInterface $logger A DebugLoggerInterface instance
|
* @param DebugLoggerInterface $logger A DebugLoggerInterface instance
|
||||||
@ -49,25 +58,20 @@ class ExceptionController
|
|||||||
public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null)
|
public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null)
|
||||||
{
|
{
|
||||||
$currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1));
|
$currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1));
|
||||||
|
$showException = $request->get('showException', $this->debug); // As opposed to an additional parameter, this maintains BC
|
||||||
|
|
||||||
return $this->createResponse($request, $exception, $this->debug, $logger, $currentContent);
|
$code = $exception->getStatusCode();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
return new Response($this->twig->render(
|
||||||
* Displays the error page for arbitrary status codes and formats.
|
$this->findTemplate($request, $request->getRequestFormat(), $code, $showException),
|
||||||
*
|
array(
|
||||||
* @param Request $request The request
|
'status_code' => $code,
|
||||||
* @param int $code The HTTP status code to show the error page for.
|
'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '',
|
||||||
*
|
'exception' => $exception,
|
||||||
* @return Response
|
'logger' => $logger,
|
||||||
*
|
'currentContent' => $currentContent,
|
||||||
* @throws \InvalidArgumentException When the error template does not exist
|
)
|
||||||
*/
|
));
|
||||||
public function testErrorPageAction(Request $request, $code)
|
|
||||||
{
|
|
||||||
$exception = FlattenException::create(new \Exception("Something has intentionally gone wrong."), $code);
|
|
||||||
|
|
||||||
return $this->createResponse($request, $exception, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,19 +94,19 @@ class ExceptionController
|
|||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param string $format
|
* @param string $format
|
||||||
* @param int $code An HTTP response status code
|
* @param int $code An HTTP response status code
|
||||||
* @param bool $debug
|
* @param bool $showException
|
||||||
*
|
*
|
||||||
* @return TemplateReferenceInterface
|
* @return TemplateReferenceInterface
|
||||||
*/
|
*/
|
||||||
protected function findTemplate(Request $request, $format, $code, $debug)
|
protected function findTemplate(Request $request, $format, $code, $showException)
|
||||||
{
|
{
|
||||||
$name = $debug ? 'exception' : 'error';
|
$name = $showException ? 'exception' : 'error';
|
||||||
if ($debug && 'html' == $format) {
|
if ($showException && 'html' == $format) {
|
||||||
$name = 'exception_full';
|
$name = 'exception_full';
|
||||||
}
|
}
|
||||||
|
|
||||||
// when not in debug, try to find a template for the specific HTTP status code and format
|
// For error pages, try to find a template for the specific HTTP status code and format
|
||||||
if (!$debug) {
|
if (!$showException) {
|
||||||
$template = new TemplateReference('TwigBundle', 'Exception', $name.$code, $format, 'twig');
|
$template = new TemplateReference('TwigBundle', 'Exception', $name.$code, $format, 'twig');
|
||||||
if ($this->templateExists($template)) {
|
if ($this->templateExists($template)) {
|
||||||
return $template;
|
return $template;
|
||||||
@ -138,29 +142,4 @@ class ExceptionController
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Request $request
|
|
||||||
* @param FlattenException $exception
|
|
||||||
* @param bool $debug
|
|
||||||
* @param DebugLoggerInterface $logger
|
|
||||||
* @param string $currentContent
|
|
||||||
*
|
|
||||||
* @return Response
|
|
||||||
*/
|
|
||||||
protected function createResponse(Request $request, FlattenException $exception, $debug, DebugLoggerInterface $logger = null, $currentContent = '')
|
|
||||||
{
|
|
||||||
$code = $exception->getStatusCode();
|
|
||||||
|
|
||||||
return new Response($this->twig->render(
|
|
||||||
(string) $this->findTemplate($request, $request->getRequestFormat(), $code, $debug),
|
|
||||||
array(
|
|
||||||
'status_code' => $code,
|
|
||||||
'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '',
|
|
||||||
'exception' => $exception,
|
|
||||||
'logger' => $logger,
|
|
||||||
'currentContent' => $currentContent,
|
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Bundle\TwigBundle\Controller;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpKernel\Exception\FlattenException;
|
||||||
|
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PreviewErrorController can be used to test error pages.
|
||||||
|
*
|
||||||
|
* It will create a test exception and forward it to another controller.
|
||||||
|
*
|
||||||
|
* @author Matthias Pigulla <mp@webfactory.de>
|
||||||
|
*/
|
||||||
|
class PreviewErrorController
|
||||||
|
{
|
||||||
|
protected $kernel;
|
||||||
|
protected $controller;
|
||||||
|
|
||||||
|
public function __construct(HttpKernelInterface $kernel, $controller)
|
||||||
|
{
|
||||||
|
$this->kernel = $kernel;
|
||||||
|
$this->controller = $controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function previewErrorPageAction(Request $request, $code)
|
||||||
|
{
|
||||||
|
$exception = FlattenException::create(new \Exception("Something has intentionally gone wrong."), $code);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This Request mimics the parameters set by
|
||||||
|
* \Symfony\Component\HttpKernel\EventListener\ExceptionListener::duplicateRequest, with
|
||||||
|
* the additional "showException" flag.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$subRequest = $request->duplicate(null, null, array(
|
||||||
|
'_controller' => $this->controller,
|
||||||
|
'exception' => $exception,
|
||||||
|
'logger' => null,
|
||||||
|
'format' => $request->getRequestFormat(),
|
||||||
|
'showException' => false,
|
||||||
|
));
|
||||||
|
|
||||||
|
return $this->kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@
|
|||||||
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
|
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
|
||||||
|
|
||||||
<route id="_twig_error_test" path="/{code}.{_format}">
|
<route id="_twig_error_test" path="/{code}.{_format}">
|
||||||
<default key="_controller">twig.controller.exception:testErrorPageAction</default>
|
<default key="_controller">twig.controller.preview_error:previewErrorPageAction</default>
|
||||||
<default key="_format">html</default>
|
<default key="_format">html</default>
|
||||||
<requirement key="code">\d+</requirement>
|
<requirement key="code">\d+</requirement>
|
||||||
</route>
|
</route>
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
<parameter key="twig.translation.extractor.class">Symfony\Bridge\Twig\Translation\TwigExtractor</parameter>
|
<parameter key="twig.translation.extractor.class">Symfony\Bridge\Twig\Translation\TwigExtractor</parameter>
|
||||||
<parameter key="twig.exception_listener.class">Symfony\Component\HttpKernel\EventListener\ExceptionListener</parameter>
|
<parameter key="twig.exception_listener.class">Symfony\Component\HttpKernel\EventListener\ExceptionListener</parameter>
|
||||||
<parameter key="twig.controller.exception.class">Symfony\Bundle\TwigBundle\Controller\ExceptionController</parameter>
|
<parameter key="twig.controller.exception.class">Symfony\Bundle\TwigBundle\Controller\ExceptionController</parameter>
|
||||||
|
<parameter key="twig.controller.preview_error.class">Symfony\Bundle\TwigBundle\Controller\PreviewErrorController</parameter>
|
||||||
</parameters>
|
</parameters>
|
||||||
|
|
||||||
<services>
|
<services>
|
||||||
@ -133,5 +134,10 @@
|
|||||||
<argument type="service" id="twig" />
|
<argument type="service" id="twig" />
|
||||||
<argument>%kernel.debug%</argument>
|
<argument>%kernel.debug%</argument>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service id="twig.controller.preview_error" class="%twig.controller.preview_error.class%">
|
||||||
|
<argument type="service" id="http_kernel" />
|
||||||
|
<argument>%twig.exception_listener.controller%</argument>
|
||||||
|
</service>
|
||||||
</services>
|
</services>
|
||||||
</container>
|
</container>
|
||||||
|
@ -13,6 +13,7 @@ namespace Symfony\Bundle\TwigBundle\Tests\Controller;
|
|||||||
|
|
||||||
use Symfony\Bundle\TwigBundle\Tests\TestCase;
|
use Symfony\Bundle\TwigBundle\Tests\TestCase;
|
||||||
use Symfony\Bundle\TwigBundle\Controller\ExceptionController;
|
use Symfony\Bundle\TwigBundle\Controller\ExceptionController;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\FlattenException;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
class ExceptionControllerTest extends TestCase
|
class ExceptionControllerTest extends TestCase
|
||||||
@ -42,27 +43,22 @@ class ExceptionControllerTest extends TestCase
|
|||||||
$controller->showAction($request, $flatten);
|
$controller->showAction($request, $flatten);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testErrorPagesInDebugMode()
|
public function testShowActionCanBeForcedToShowErrorPage()
|
||||||
{
|
{
|
||||||
$twig = new \Twig_Environment(
|
$twig = new \Twig_Environment(
|
||||||
new \Twig_Loader_Array(array(
|
new \Twig_Loader_Array(array(
|
||||||
'TwigBundle:Exception:error404.html.twig' => '
|
'TwigBundle:Exception:error404.html.twig' => 'ok',
|
||||||
{%- if exception is defined and status_text is defined and status_code is defined -%}
|
|
||||||
OK
|
|
||||||
{%- else -%}
|
|
||||||
"exception" variable is missing
|
|
||||||
{%- endif -%}
|
|
||||||
',
|
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
$request = Request::create('whatever');
|
$request = Request::create('whatever', 'GET', array('showException' => false));
|
||||||
|
$exception = FlattenException::create(new \Exception(), 404);
|
||||||
|
$controller = new ExceptionController($twig, /* "showException" defaults to --> */ true);
|
||||||
|
|
||||||
$controller = new ExceptionController($twig, /* "debug" set to --> */ true);
|
$response = $controller->showAction($request, $exception, null);
|
||||||
$response = $controller->testErrorPageAction($request, 404);
|
|
||||||
|
|
||||||
$this->assertEquals(200, $response->getStatusCode()); // successful request
|
$this->assertEquals(200, $response->getStatusCode()); // successful request
|
||||||
$this->assertEquals('OK', $response->getContent()); // content of the error404.html template
|
$this->assertEquals('ok', $response->getContent()); // content of the error404.html template
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFallbackToHtmlIfNoTemplateForRequestedFormat()
|
public function testFallbackToHtmlIfNoTemplateForRequestedFormat()
|
||||||
@ -75,9 +71,10 @@ class ExceptionControllerTest extends TestCase
|
|||||||
|
|
||||||
$request = Request::create('whatever');
|
$request = Request::create('whatever');
|
||||||
$request->setRequestFormat('txt');
|
$request->setRequestFormat('txt');
|
||||||
|
$exception = FlattenException::create(new \Exception());
|
||||||
$controller = new ExceptionController($twig, false);
|
$controller = new ExceptionController($twig, false);
|
||||||
$response = $controller->testErrorPageAction($request, 42);
|
|
||||||
|
$response = $controller->showAction($request, $exception);
|
||||||
|
|
||||||
$this->assertEquals('html', $request->getRequestFormat());
|
$this->assertEquals('html', $request->getRequestFormat());
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Bundle\TwigBundle\Tests\Controller;
|
||||||
|
|
||||||
|
use Symfony\Bundle\TwigBundle\Controller\PreviewErrorController;
|
||||||
|
use Symfony\Bundle\TwigBundle\Tests\TestCase;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||||
|
|
||||||
|
class PreviewErrorControllerTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testForwardRequestToConfiguredController()
|
||||||
|
{
|
||||||
|
$self = $this;
|
||||||
|
|
||||||
|
$request = Request::create('whatever');
|
||||||
|
$response = new Response("");
|
||||||
|
$code = 123;
|
||||||
|
$logicalControllerName = 'foo:bar:baz';
|
||||||
|
|
||||||
|
$kernel = $this->getMock('\Symfony\Component\HttpKernel\HttpKernelInterface');
|
||||||
|
$kernel
|
||||||
|
->expects($this->once())
|
||||||
|
->method('handle')
|
||||||
|
->with(
|
||||||
|
$this->callback(function (Request $request) use ($self, $logicalControllerName, $code) {
|
||||||
|
|
||||||
|
$self->assertEquals($logicalControllerName, $request->attributes->get('_controller'));
|
||||||
|
|
||||||
|
$exception = $request->attributes->get('exception');
|
||||||
|
$self->assertInstanceOf('Symfony\Component\HttpKernel\Exception\FlattenException', $exception);
|
||||||
|
$self->assertEquals($code, $exception->getStatusCode());
|
||||||
|
|
||||||
|
$self->assertFalse($request->attributes->get('showException'));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}),
|
||||||
|
$this->equalTo(HttpKernelInterface::SUB_REQUEST)
|
||||||
|
)
|
||||||
|
->will($this->returnValue($response));
|
||||||
|
|
||||||
|
$controller = new PreviewErrorController($kernel, $logicalControllerName);
|
||||||
|
|
||||||
|
$this->assertSame($response, $controller->previewErrorPageAction($request, $code));
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user