diff --git a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php index 1bd758de5b..395f69785f 100644 --- a/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php +++ b/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php @@ -19,7 +19,8 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Templating\TemplateReferenceInterface; /** - * ExceptionController. + * ExceptionController renders error or exception pages for a given + * FlattenException. * * @author Fabien Potencier * @author Matthias Pigulla @@ -27,6 +28,10 @@ use Symfony\Component\Templating\TemplateReferenceInterface; class ExceptionController { protected $twig; + + /** + * @var bool Show error (false) or exception (true) pages by default. + */ protected $debug; public function __construct(\Twig_Environment $twig, $debug) @@ -38,6 +43,10 @@ class ExceptionController /** * 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 FlattenException $exception A FlattenException instance * @param DebugLoggerInterface $logger A DebugLoggerInterface instance @@ -49,25 +58,20 @@ class ExceptionController public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null) { $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(); - /** - * Displays the error page for arbitrary status codes and formats. - * - * @param Request $request The request - * @param int $code The HTTP status code to show the error page for. - * - * @return Response - * - * @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); + return new Response($this->twig->render( + $this->findTemplate($request, $request->getRequestFormat(), $code, $showException), + array( + 'status_code' => $code, + 'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '', + 'exception' => $exception, + 'logger' => $logger, + 'currentContent' => $currentContent, + ) + )); } /** @@ -90,19 +94,19 @@ class ExceptionController * @param Request $request * @param string $format * @param int $code An HTTP response status code - * @param bool $debug + * @param bool $showException * * @return TemplateReferenceInterface */ - protected function findTemplate(Request $request, $format, $code, $debug) + protected function findTemplate(Request $request, $format, $code, $showException) { - $name = $debug ? 'exception' : 'error'; - if ($debug && 'html' == $format) { + $name = $showException ? 'exception' : 'error'; + if ($showException && 'html' == $format) { $name = 'exception_full'; } - // when not in debug, try to find a template for the specific HTTP status code and format - if (!$debug) { + // For error pages, try to find a template for the specific HTTP status code and format + if (!$showException) { $template = new TemplateReference('TwigBundle', 'Exception', $name.$code, $format, 'twig'); if ($this->templateExists($template)) { return $template; @@ -138,29 +142,4 @@ class ExceptionController 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, - ) - )); - } } diff --git a/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php b/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php new file mode 100644 index 0000000000..b4eb932e58 --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php @@ -0,0 +1,56 @@ + + * + * 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 + */ +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); + } +} diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/routing/errors.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/routing/errors.xml index 493af74c01..bf87f8be7a 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/routing/errors.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/routing/errors.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd"> - twig.controller.exception:testErrorPageAction + twig.controller.preview_error:previewErrorPageAction html \d+ diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml index c10dc942b1..3c81983dd3 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml @@ -25,6 +25,7 @@ Symfony\Bridge\Twig\Translation\TwigExtractor Symfony\Component\HttpKernel\EventListener\ExceptionListener Symfony\Bundle\TwigBundle\Controller\ExceptionController + Symfony\Bundle\TwigBundle\Controller\PreviewErrorController @@ -133,5 +134,10 @@ %kernel.debug% + + + + %twig.exception_listener.controller% + diff --git a/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php b/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php index 39e17cb221..222ac32c9e 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php @@ -13,6 +13,7 @@ namespace Symfony\Bundle\TwigBundle\Tests\Controller; use Symfony\Bundle\TwigBundle\Tests\TestCase; use Symfony\Bundle\TwigBundle\Controller\ExceptionController; +use Symfony\Component\HttpKernel\Exception\FlattenException; use Symfony\Component\HttpFoundation\Request; class ExceptionControllerTest extends TestCase @@ -42,27 +43,22 @@ class ExceptionControllerTest extends TestCase $controller->showAction($request, $flatten); } - public function testErrorPagesInDebugMode() + public function testShowActionCanBeForcedToShowErrorPage() { $twig = new \Twig_Environment( new \Twig_Loader_Array(array( - 'TwigBundle:Exception:error404.html.twig' => ' - {%- if exception is defined and status_text is defined and status_code is defined -%} - OK - {%- else -%} - "exception" variable is missing - {%- endif -%} - ', + 'TwigBundle:Exception:error404.html.twig' => 'ok', )) ); - $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->testErrorPageAction($request, 404); + $response = $controller->showAction($request, $exception, null); $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() @@ -75,9 +71,10 @@ class ExceptionControllerTest extends TestCase $request = Request::create('whatever'); $request->setRequestFormat('txt'); - + $exception = FlattenException::create(new \Exception()); $controller = new ExceptionController($twig, false); - $response = $controller->testErrorPageAction($request, 42); + + $response = $controller->showAction($request, $exception); $this->assertEquals('html', $request->getRequestFormat()); } diff --git a/src/Symfony/Bundle/TwigBundle/Tests/Controller/PreviewErrorControllerTest.php b/src/Symfony/Bundle/TwigBundle/Tests/Controller/PreviewErrorControllerTest.php new file mode 100644 index 0000000000..2d97b0c7ad --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/Tests/Controller/PreviewErrorControllerTest.php @@ -0,0 +1,56 @@ + + * + * 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)); + } +}