Skip to content

Commit c7b899c

Browse files
committed
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
2 parents f288a69 + 2065e00 commit c7b899c

File tree

6 files changed

+158
-64
lines changed

6 files changed

+158
-64
lines changed

src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php

Lines changed: 28 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,19 @@
1919
use Symfony\Component\Templating\TemplateReferenceInterface;
2020

2121
/**
22-
* ExceptionController.
22+
* ExceptionController renders error or exception pages for a given
23+
* FlattenException.
2324
*
2425
* @author Fabien Potencier <fabien@symfony.com>
2526
* @author Matthias Pigulla <mp@webfactory.de>
2627
*/
2728
class ExceptionController
2829
{
2930
protected $twig;
31+
32+
/**
33+
* @var bool Show error (false) or exception (true) pages by default.
34+
*/
3035
protected $debug;
3136

3237
public function __construct(\Twig_Environment $twig, $debug)
@@ -38,6 +43,10 @@ public function __construct(\Twig_Environment $twig, $debug)
3843
/**
3944
* Converts an Exception to a Response.
4045
*
46+
* A "showException" request parameter can be used to force display of an error page (when set to false) or
47+
* the exception page (when true). If it is not present, the "debug" value passed into the constructor will
48+
* be used.
49+
*
4150
* @param Request $request The request
4251
* @param FlattenException $exception A FlattenException instance
4352
* @param DebugLoggerInterface $logger A DebugLoggerInterface instance
@@ -49,25 +58,20 @@ public function __construct(\Twig_Environment $twig, $debug)
4958
public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null)
5059
{
5160
$currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1));
61+
$showException = $request->get('showException', $this->debug); // As opposed to an additional parameter, this maintains BC
5262

53-
return $this->createResponse($request, $exception, $this->debug, $logger, $currentContent);
54-
}
55-
56-
/**
57-
* Displays the error page for arbitrary status codes and formats.
58-
*
59-
* @param Request $request The request
60-
* @param int $code The HTTP status code to show the error page for.
61-
*
62-
* @return Response
63-
*
64-
* @throws \InvalidArgumentException When the error template does not exist
65-
*/
66-
public function testErrorPageAction(Request $request, $code)
67-
{
68-
$exception = FlattenException::create(new \Exception("Something has intentionally gone wrong."), $code);
63+
$code = $exception->getStatusCode();
6964

70-
return $this->createResponse($request, $exception, false);
65+
return new Response($this->twig->render(
66+
$this->findTemplate($request, $request->getRequestFormat(), $code, $showException),
67+
array(
68+
'status_code' => $code,
69+
'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '',
70+
'exception' => $exception,
71+
'logger' => $logger,
72+
'currentContent' => $currentContent,
73+
)
74+
));
7175
}
7276

7377
/**
@@ -90,19 +94,19 @@ protected function getAndCleanOutputBuffering($startObLevel)
9094
* @param Request $request
9195
* @param string $format
9296
* @param int $code An HTTP response status code
93-
* @param bool $debug
97+
* @param bool $showException
9498
*
9599
* @return TemplateReferenceInterface
96100
*/
97-
protected function findTemplate(Request $request, $format, $code, $debug)
101+
protected function findTemplate(Request $request, $format, $code, $showException)
98102
{
99-
$name = $debug ? 'exception' : 'error';
100-
if ($debug && 'html' == $format) {
103+
$name = $showException ? 'exception' : 'error';
104+
if ($showException && 'html' == $format) {
101105
$name = 'exception_full';
102106
}
103107

104-
// when not in debug, try to find a template for the specific HTTP status code and format
105-
if (!$debug) {
108+
// For error pages, try to find a template for the specific HTTP status code and format
109+
if (!$showException) {
106110
$template = new TemplateReference('TwigBundle', 'Exception', $name.$code, $format, 'twig');
107111
if ($this->templateExists($template)) {
108112
return $template;
@@ -138,29 +142,4 @@ protected function templateExists($template)
138142

139143
return false;
140144
}
141-
142-
/**
143-
* @param Request $request
144-
* @param FlattenException $exception
145-
* @param bool $debug
146-
* @param DebugLoggerInterface $logger
147-
* @param string $currentContent
148-
*
149-
* @return Response
150-
*/
151-
protected function createResponse(Request $request, FlattenException $exception, $debug, DebugLoggerInterface $logger = null, $currentContent = '')
152-
{
153-
$code = $exception->getStatusCode();
154-
155-
return new Response($this->twig->render(
156-
(string) $this->findTemplate($request, $request->getRequestFormat(), $code, $debug),
157-
array(
158-
'status_code' => $code,
159-
'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '',
160-
'exception' => $exception,
161-
'logger' => $logger,
162-
'currentContent' => $currentContent,
163-
)
164-
));
165-
}
166145
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\TwigBundle\Controller;
13+
14+
use Symfony\Component\HttpKernel\Exception\FlattenException;
15+
use Symfony\Component\HttpKernel\HttpKernelInterface;
16+
use Symfony\Component\HttpFoundation\Request;
17+
18+
/**
19+
* PreviewErrorController can be used to test error pages.
20+
*
21+
* It will create a test exception and forward it to another controller.
22+
*
23+
* @author Matthias Pigulla <mp@webfactory.de>
24+
*/
25+
class PreviewErrorController
26+
{
27+
protected $kernel;
28+
protected $controller;
29+
30+
public function __construct(HttpKernelInterface $kernel, $controller)
31+
{
32+
$this->kernel = $kernel;
33+
$this->controller = $controller;
34+
}
35+
36+
public function previewErrorPageAction(Request $request, $code)
37+
{
38+
$exception = FlattenException::create(new \Exception("Something has intentionally gone wrong."), $code);
39+
40+
/*
41+
* This Request mimics the parameters set by
42+
* \Symfony\Component\HttpKernel\EventListener\ExceptionListener::duplicateRequest, with
43+
* the additional "showException" flag.
44+
*/
45+
46+
$subRequest = $request->duplicate(null, null, array(
47+
'_controller' => $this->controller,
48+
'exception' => $exception,
49+
'logger' => null,
50+
'format' => $request->getRequestFormat(),
51+
'showException' => false,
52+
));
53+
54+
return $this->kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
55+
}
56+
}

src/Symfony/Bundle/TwigBundle/Resources/config/routing/errors.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
66

77
<route id="_twig_error_test" path="/{code}.{_format}">
8-
<default key="_controller">twig.controller.exception:testErrorPageAction</default>
8+
<default key="_controller">twig.controller.preview_error:previewErrorPageAction</default>
99
<default key="_format">html</default>
1010
<requirement key="code">\d+</requirement>
1111
</route>

src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<parameter key="twig.translation.extractor.class">Symfony\Bridge\Twig\Translation\TwigExtractor</parameter>
2626
<parameter key="twig.exception_listener.class">Symfony\Component\HttpKernel\EventListener\ExceptionListener</parameter>
2727
<parameter key="twig.controller.exception.class">Symfony\Bundle\TwigBundle\Controller\ExceptionController</parameter>
28+
<parameter key="twig.controller.preview_error.class">Symfony\Bundle\TwigBundle\Controller\PreviewErrorController</parameter>
2829
</parameters>
2930

3031
<services>
@@ -133,5 +134,10 @@
133134
<argument type="service" id="twig" />
134135
<argument>%kernel.debug%</argument>
135136
</service>
137+
138+
<service id="twig.controller.preview_error" class="%twig.controller.preview_error.class%">
139+
<argument type="service" id="http_kernel" />
140+
<argument>%twig.exception_listener.controller%</argument>
141+
</service>
136142
</services>
137143
</container>

src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Bundle\TwigBundle\Tests\TestCase;
1515
use Symfony\Bundle\TwigBundle\Controller\ExceptionController;
16+
use Symfony\Component\HttpKernel\Exception\FlattenException;
1617
use Symfony\Component\HttpFoundation\Request;
1718

1819
class ExceptionControllerTest extends TestCase
@@ -42,27 +43,22 @@ public function testOnlyClearOwnOutputBuffers()
4243
$controller->showAction($request, $flatten);
4344
}
4445

45-
public function testErrorPagesInDebugMode()
46+
public function testShowActionCanBeForcedToShowErrorPage()
4647
{
4748
$twig = new \Twig_Environment(
4849
new \Twig_Loader_Array(array(
49-
'TwigBundle:Exception:error404.html.twig' => '
50-
{%- if exception is defined and status_text is defined and status_code is defined -%}
51-
OK
52-
{%- else -%}
53-
"exception" variable is missing
54-
{%- endif -%}
55-
',
50+
'TwigBundle:Exception:error404.html.twig' => 'ok',
5651
))
5752
);
5853

59-
$request = Request::create('whatever');
54+
$request = Request::create('whatever', 'GET', array('showException' => false));
55+
$exception = FlattenException::create(new \Exception(), 404);
56+
$controller = new ExceptionController($twig, /* "showException" defaults to --> */ true);
6057

61-
$controller = new ExceptionController($twig, /* "debug" set to --> */ true);
62-
$response = $controller->testErrorPageAction($request, 404);
58+
$response = $controller->showAction($request, $exception, null);
6359

6460
$this->assertEquals(200, $response->getStatusCode()); // successful request
65-
$this->assertEquals('OK', $response->getContent()); // content of the error404.html template
61+
$this->assertEquals('ok', $response->getContent()); // content of the error404.html template
6662
}
6763

6864
public function testFallbackToHtmlIfNoTemplateForRequestedFormat()
@@ -75,9 +71,10 @@ public function testFallbackToHtmlIfNoTemplateForRequestedFormat()
7571

7672
$request = Request::create('whatever');
7773
$request->setRequestFormat('txt');
78-
74+
$exception = FlattenException::create(new \Exception());
7975
$controller = new ExceptionController($twig, false);
80-
$response = $controller->testErrorPageAction($request, 42);
76+
77+
$response = $controller->showAction($request, $exception);
8178

8279
$this->assertEquals('html', $request->getRequestFormat());
8380
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\TwigBundle\Tests\Controller;
13+
14+
use Symfony\Bundle\TwigBundle\Controller\PreviewErrorController;
15+
use Symfony\Bundle\TwigBundle\Tests\TestCase;
16+
use Symfony\Component\HttpFoundation\Response;
17+
use Symfony\Component\HttpFoundation\Request;
18+
use Symfony\Component\HttpKernel\HttpKernelInterface;
19+
20+
class PreviewErrorControllerTest extends TestCase
21+
{
22+
public function testForwardRequestToConfiguredController()
23+
{
24+
$self = $this;
25+
26+
$request = Request::create('whatever');
27+
$response = new Response("");
28+
$code = 123;
29+
$logicalControllerName = 'foo:bar:baz';
30+
31+
$kernel = $this->getMock('\Symfony\Component\HttpKernel\HttpKernelInterface');
32+
$kernel
33+
->expects($this->once())
34+
->method('handle')
35+
->with(
36+
$this->callback(function (Request $request) use ($self, $logicalControllerName, $code) {
37+
38+
$self->assertEquals($logicalControllerName, $request->attributes->get('_controller'));
39+
40+
$exception = $request->attributes->get('exception');
41+
$self->assertInstanceOf('Symfony\Component\HttpKernel\Exception\FlattenException', $exception);
42+
$self->assertEquals($code, $exception->getStatusCode());
43+
44+
$self->assertFalse($request->attributes->get('showException'));
45+
46+
return true;
47+
}),
48+
$this->equalTo(HttpKernelInterface::SUB_REQUEST)
49+
)
50+
->will($this->returnValue($response));
51+
52+
$controller = new PreviewErrorController($kernel, $logicalControllerName);
53+
54+
$this->assertSame($response, $controller->previewErrorPageAction($request, $code));
55+
}
56+
}

0 commit comments

Comments
 (0)