Commit b6dca08c authored by Fabien Potencier's avatar Fabien Potencier

added a default exception handler

parent e2b52f6c
...@@ -262,3 +262,8 @@ Core parameters ...@@ -262,3 +262,8 @@ Core parameters
Defaults to 443. Defaults to 443.
This parameter can be used by the ``UrlGeneratorExtension``. This parameter can be used by the ``UrlGeneratorExtension``.
* **debug** (optional): Returns whether or not the application is running in
debug mode.
Defaults to false.
...@@ -40,6 +40,13 @@ are using apache you can use a ``.htaccess`` file for this. ...@@ -40,6 +40,13 @@ are using apache you can use a ``.htaccess`` file for this.
``RewriteBase`` statement and adjust the path to point to your directory, ``RewriteBase`` statement and adjust the path to point to your directory,
relative from the webroot. relative from the webroot.
.. tip::
When developing a website, you might want to turn on the debug mode to
ease debugging::
$app['debug'] = true;
Routing Routing
------- -------
...@@ -347,6 +354,13 @@ If some part of your code throws an exception you will want to display ...@@ -347,6 +354,13 @@ If some part of your code throws an exception you will want to display
some kind of error page to the user. This is what error handlers do. You some kind of error page to the user. This is what error handlers do. You
can also use them to do additional things, such as logging. can also use them to do additional things, such as logging.
.. note::
Silex comes with a default error handler that displays a detailed error
message with the stack trace when **debug** is true, and a simple error
message otherwise. Error handlers registered via the ``error()`` method
always take precedence.
To register an error handler, pass a closure to the ``error`` method To register an error handler, pass a closure to the ``error`` method
which takes an ``Exception`` argument and returns a response:: which takes an ``Exception`` argument and returns a response::
...@@ -360,7 +374,7 @@ You can also check for specific errors by using ``instanceof``, and handle ...@@ -360,7 +374,7 @@ You can also check for specific errors by using ``instanceof``, and handle
them differently:: them differently::
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
$app->error(function (\Exception $e) { $app->error(function (\Exception $e) {
...@@ -368,7 +382,8 @@ them differently:: ...@@ -368,7 +382,8 @@ them differently::
return new Response('The requested page could not be found.', 404); return new Response('The requested page could not be found.', 404);
} }
$code = ($e instanceof HttpException) ? $e->getStatusCode() : 500; $code = $e instanceof HttpExceptionInterface ? $e->getStatusCode() : 500;
return new Response('We are sorry, but something went terribly wrong.', $code); return new Response('We are sorry, but something went terribly wrong.', $code);
}); });
......
...@@ -66,9 +66,16 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe ...@@ -66,9 +66,16 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
return new ControllerCollection($app['routes']); return new ControllerCollection($app['routes']);
}); });
$this['exception_handler'] = $this->share(function () {
return new ExceptionHandler();
});
$this['dispatcher'] = $this->share(function () use ($app) { $this['dispatcher'] = $this->share(function () use ($app) {
$dispatcher = new EventDispatcher(); $dispatcher = new EventDispatcher();
$dispatcher->addSubscriber($app); $dispatcher->addSubscriber($app);
if (isset($app['exception_handler'])) {
$dispatcher->addSubscriber($app['exception_handler']);
}
return $dispatcher; return $dispatcher;
}); });
...@@ -83,6 +90,7 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe ...@@ -83,6 +90,7 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
$this['request.http_port'] = 80; $this['request.http_port'] = 80;
$this['request.https_port'] = 443; $this['request.https_port'] = 443;
$this['debug'] = false;
} }
/** /**
......
<?php
/*
* This file is part of the Silex framework.
*
* (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 Silex;
use Silex\Application;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Debug\ExceptionHandler as DebugExceptionHandler;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Defaults exception handler.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ExceptionHandler implements EventSubscriberInterface
{
public function onSilexError(GetResponseForErrorEvent $event)
{
$app = $event->getKernel();
$exception = $event->getException();
$code = $exception instanceof HttpExceptionInterface ? $exception->getStatusCode() : 500;
if ($app['debug']) {
$handler = new DebugExceptionHandler();
$response = new Response($handler->getErrorMessage($exception), $code);
} else {
$title = 'Whoops, looks like something went wrong.';
if ($exception instanceof NotFoundHttpException) {
$title = 'Sorry, the page you are looking for could not be found.';
}
$response = new Response(sprintf('<!DOCTYPE html><html><head><meta charset="utf-8"><title>%s</title></head><body><h1>%s</h1></body></html>', $title, $title), $code);
}
$event->setResponse($response);
}
/**
* {@inheritdoc}
*/
static public function getSubscribedEvents()
{
return array(SilexEvents::ERROR => array('onSilexError', -255));
}
}
...@@ -23,9 +23,62 @@ use Symfony\Component\HttpFoundation\Response; ...@@ -23,9 +23,62 @@ use Symfony\Component\HttpFoundation\Response;
*/ */
class ErrorHandlerTest extends \PHPUnit_Framework_TestCase class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
{ {
public function testErrorHandlerExceptionNoDebug()
{
$app = new Application();
$app['debug'] = false;
$app->match('/foo', function () {
throw new \RuntimeException('foo exception');
});
$request = Request::create('/foo');
$response = $app->handle($request);
$this->assertContains('<title>Whoops, looks like something went wrong.</title>', $response->getContent());
$this->assertEquals(500, $response->getStatusCode());
}
public function testErrorHandlerExceptionDebug()
{
$app = new Application();
$app['debug'] = true;
$app->match('/foo', function () {
throw new \RuntimeException('foo exception');
});
$request = Request::create('/foo');
$response = $app->handle($request);
$this->assertContains('<title>foo exception (500 Internal Server Error)</title>', $response->getContent());
$this->assertEquals(500, $response->getStatusCode());
}
public function testErrorHandlerNotFoundNoDebug()
{
$app = new Application();
$app['debug'] = false;
$request = Request::create('/foo');
$response = $app->handle($request);
$this->assertContains('<title>Sorry, the page you are looking for could not be found.</title>', $response->getContent());
$this->assertEquals(404, $response->getStatusCode());
}
public function testErrorHandlerNotFoundDebug()
{
$app = new Application();
$app['debug'] = true;
$request = Request::create('/foo');
$response = $app->handle($request);
$this->assertContains('<title>No route found for "GET /foo" (500 Internal Server Error)</title>', $response->getContent());
$this->assertEquals(404, $response->getStatusCode());
}
public function testNoErrorHandler() public function testNoErrorHandler()
{ {
$app = new Application(); $app = new Application();
unset($app['exception_handler']);
$app->match('/foo', function () { $app->match('/foo', function () {
throw new \RuntimeException('foo exception'); throw new \RuntimeException('foo exception');
...@@ -93,6 +146,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase ...@@ -93,6 +146,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
public function testNoResponseErrorHandler() public function testNoResponseErrorHandler()
{ {
$app = new Application(); $app = new Application();
unset($app['exception_handler']);
$app->match('/foo', function () { $app->match('/foo', function () {
throw new \RuntimeException('foo exception'); throw new \RuntimeException('foo exception');
......
...@@ -118,6 +118,7 @@ EOF ...@@ -118,6 +118,7 @@ EOF
$mounted = new LazyApplication($tmp); $mounted = new LazyApplication($tmp);
$app = new Application(); $app = new Application();
unset($app['exception_handler']);
$app->mount('/hello', $mounted); $app->mount('/hello', $mounted);
try { try {
......
...@@ -102,6 +102,7 @@ class RouterTest extends \PHPUnit_Framework_TestCase ...@@ -102,6 +102,7 @@ class RouterTest extends \PHPUnit_Framework_TestCase
public function testMissingRoute() public function testMissingRoute()
{ {
$app = new Application(); $app = new Application();
unset($app['exception_handler']);
$request = Request::create('/baz'); $request = Request::create('/baz');
$app->handle($request); $app->handle($request);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment