Commit 6c08bd25 authored by Igor Wiedler's avatar Igor Wiedler

nice error handler, including tests; move routing tests to RouterTest

parent c1b161cc
...@@ -29,6 +29,7 @@ use Symfony\Component\Routing\Matcher\UrlMatcher; ...@@ -29,6 +29,7 @@ use Symfony\Component\Routing\Matcher\UrlMatcher;
class Framework extends HttpKernel class Framework extends HttpKernel
{ {
protected $routes; protected $routes;
protected $errorHandlers = array();
public function __construct(array $map = null) public function __construct(array $map = null)
{ {
...@@ -41,6 +42,7 @@ class Framework extends HttpKernel ...@@ -41,6 +42,7 @@ class Framework extends HttpKernel
$dispatcher = new EventDispatcher(); $dispatcher = new EventDispatcher();
$dispatcher->connect('core.request', array($this, 'parseRequest')); $dispatcher->connect('core.request', array($this, 'parseRequest'));
$dispatcher->connect('core.view', array($this, 'parseResponse')); $dispatcher->connect('core.view', array($this, 'parseResponse'));
$dispatcher->connect('core.exception', array($this, 'handleException'));
$resolver = new ControllerResolver(); $resolver = new ControllerResolver();
parent::__construct($dispatcher, $resolver); parent::__construct($dispatcher, $resolver);
...@@ -88,6 +90,13 @@ class Framework extends HttpKernel ...@@ -88,6 +90,13 @@ class Framework extends HttpKernel
return $this; return $this;
} }
public function error($callback)
{
$this->errorHandlers[] = $callback;
return $this;
}
public function run(Request $request = null) public function run(Request $request = null)
{ {
if (null === $request) { if (null === $request) {
...@@ -137,6 +146,23 @@ class Framework extends HttpKernel ...@@ -137,6 +146,23 @@ class Framework extends HttpKernel
return $response; return $response;
} }
// use the first non-null handler response
// but execute all error handlers
public function handleException(Event $event)
{
$exception = $event->get('exception');
$prevResult = null;
foreach ($this->errorHandlers as $callback) {
$result = $callback($exception);
if (null !== $result && !$prevResult) {
$response = $this->parseResponse($event, $result);
$event->setReturnValue($response);
$event->setProcessed(true);
$prevResult = $result;
}
}
}
public static function create(array $map = null) public static function create(array $map = null)
{ {
return new static($map); return new static($map);
......
<?php
namespace Silex\Tests;
use Silex\Framework;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/*
* This file is part of the Silex framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.org>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* Error handler test cases.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.org>
*/
class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
{
public function testNoErrorHandler()
{
$framework = Framework::create(array(
'/foo' => function() {
throw new \RuntimeException('foo exception');
},
));
try {
$request = Request::create('http://test.com/foo');
$framework->handle($request);
$this->fail('->handle() should not catch exceptions where no error handler was supplied');
} catch (\RuntimeException $e) {
$this->assertEquals('foo exception', $e->getMessage());
}
}
public function testOneErrorHandler()
{
$framework = Framework::create(array(
'/foo' => function() {
throw new \RuntimeException('foo exception');
},
));
$framework->error(function($e) {
return new Response('foo exception handler');
});
$request = Request::create('http://test.com/foo');
$this->checkRouteResponse($framework, '/foo', 'foo exception handler');
}
public function testMultipleErrorHandlers()
{
$framework = Framework::create(array(
'/foo' => function() {
throw new \RuntimeException('foo exception');
},
));
$errors = 0;
$framework->error(function($e) use (&$errors) {
$errors++;
});
$framework->error(function($e) use (&$errors) {
$errors++;
return new Response('foo exception handler');
});
$framework->error(function($e) use (&$errors) {
$errors++;
return new Response('foo exception handler 2');
});
$request = Request::create('http://test.com/foo');
$this->checkRouteResponse($framework, '/foo', 'foo exception handler', 'should return the first response returned by an exception handler');
$this->assertEquals(3, $errors, 'should execute all error handlers');
}
public function testNoResponseErrorHandler()
{
$framework = Framework::create(array(
'/foo' => function() {
throw new \RuntimeException('foo exception');
},
));
$errors = 0;
$framework->error(function($e) use (&$errors) {
$errors++;
});
try {
$request = Request::create('http://test.com/foo');
$framework->handle($request);
$this->fail('->handle() should not catch exceptions where an empty error handler was supplied');
} catch (\RuntimeException $e) {
$this->assertEquals('foo exception', $e->getMessage());
}
$this->assertEquals(1, $errors, 'should execute the error handler');
}
public function testStringResponseErrorHandler()
{
$framework = Framework::create(array(
'/foo' => function() {
throw new \RuntimeException('foo exception');
},
));
$framework->error(function($e) {
return 'foo exception handler';
});
$request = Request::create('http://test.com/foo');
$this->checkRouteResponse($framework, '/foo', 'foo exception handler', 'should accept a string response from the error handler');
}
public function testErrorHandlerException()
{
$framework = Framework::create(array(
'/foo' => function() {
throw new \RuntimeException('foo exception');
},
));
$framework->error(function($e) {
throw new \RuntimeException('foo exception handler exception');
});
try {
$request = Request::create('http://test.com/foo');
$this->checkRouteResponse($framework, '/foo', 'foo exception handler', 'should accept a string response from the error handler');
$this->fail('->handle() should not catch exceptions thrown from an error handler');
} catch (\RuntimeException $e) {
$this->assertEquals('foo exception handler exception', $e->getMessage());
}
}
protected function checkRouteResponse($framework, $path, $expectedContent, $method = 'get', $message = null)
{
$request = Request::create('http://test.com' . $path, $method);
$response = $framework->handle($request);
$this->assertEquals($expectedContent, $response->getContent(), $message);
}
}
...@@ -4,10 +4,6 @@ namespace Silex\Tests; ...@@ -4,10 +4,6 @@ namespace Silex\Tests;
use Silex\Framework; use Silex\Framework;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/* /*
* This file is part of the Silex framework. * This file is part of the Silex framework.
* *
...@@ -18,7 +14,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; ...@@ -18,7 +14,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
*/ */
/** /**
* The Silex framework class. * Framework test cases.
* *
* @author Fabien Potencier <fabien.potencier@symfony-project.org> * @author Fabien Potencier <fabien.potencier@symfony-project.org>
*/ */
...@@ -30,186 +26,26 @@ class FrameworkTest extends \PHPUnit_Framework_TestCase ...@@ -30,186 +26,26 @@ class FrameworkTest extends \PHPUnit_Framework_TestCase
$this->assertInstanceOf('Silex\Framework', $framework, "Framework::create() must return instance of Framework"); $this->assertInstanceOf('Silex\Framework', $framework, "Framework::create() must return instance of Framework");
} }
public function testMapRouting() public function testFluidInterface()
{
$framework = Framework::create(array(
'/foo' => function() {
return 'foo';
},
'/bar' => function() {
return 'bar';
},
'/' => function() {
return 'root';
},
));
$this->checkRouteResponse($framework, '/foo', 'foo');
$this->checkRouteResponse($framework, '/bar', 'bar');
$this->checkRouteResponse($framework, '/', 'root');
}
public function testMapRoutingMethods()
{
$framework = Framework::create(array(
'GET /foo' => function() {
return 'foo';
},
'PUT|DELETE /bar' => function() {
return 'bar';
},
'/' => function() {
return 'root';
},
));
// foo route
$this->checkRouteResponse($framework, '/foo', 'foo');
// bar route
$this->checkRouteResponse($framework, '/bar', 'bar', 'put');
$this->checkRouteResponse($framework, '/bar', 'bar', 'delete');
// root route
$this->checkRouteResponse($framework, '/', 'root');
$this->checkRouteResponse($framework, '/', 'root', 'post');
$this->checkRouteResponse($framework, '/', 'root', 'put');
$this->checkRouteResponse($framework, '/', 'root', 'delete');
try {
$request = Request::create('http://test.com/bar');
$framework->handle($request);
$this->fail('Framework must reject HTTP GET method to /bar');
} catch (NotFoundHttpException $expected) {
}
try {
$request = Request::create('http://test.com/bar', 'post');
$framework->handle($request);
$this->fail('Framework must reject HTTP POST method to /bar');
} catch (NotFoundHttpException $expected) {
}
}
public function testMapRoutingParameters()
{
$framework = Framework::create(array(
'/hello' => function() {
return "Hello anon";
},
'/hello/:name' => function($name) {
return "Hello $name";
},
'/goodbye/:name' => function($name) {
return "Goodbye $name";
},
'/tell/:name/:message' => function($message, $name) {
return "Message for $name: $message";
},
'/' => function() {
return 'root';
},
));
$this->checkRouteResponse($framework, '/hello', 'Hello anon');
$this->checkRouteResponse($framework, '/hello/alice', 'Hello alice');
$this->checkRouteResponse($framework, '/hello/bob', 'Hello bob');
$this->checkRouteResponse($framework, '/goodbye/alice', 'Goodbye alice');
$this->checkRouteResponse($framework, '/goodbye/bob', 'Goodbye bob');
$this->checkRouteResponse($framework, '/tell/bob/secret', 'Message for bob: secret');
$this->checkRouteResponse($framework, '/', 'root');
}
public function testStatusCode()
{
$framework = Framework::create(array(
'PUT /created' => function() {
return new Response('', 201);
},
'/forbidden' => function() {
return new Response('', 403);
},
'/not_found' => function() {
return new Response('', 404);
},
));
$request = Request::create('http://test.com/created', 'put');
$response = $framework->handle($request);
$this->assertEquals(201, $response->getStatusCode());
$request = Request::create('http://test.com/forbidden');
$response = $framework->handle($request);
$this->assertEquals(403, $response->getStatusCode());
$request = Request::create('http://test.com/not_found');
$response = $framework->handle($request);
$this->assertEquals(404, $response->getStatusCode());
}
public function testRedirect()
{ {
$framework = Framework::create(array( $framework = Framework::create();
'/redirect' => function() {
$response = new Response();
$response->setRedirect('/target');
return $response;
},
));
$request = Request::create('http://test.com/redirect'); $returnValue = $framework->match('/foo', function() {});
$response = $framework->handle($request); $this->assertSame($framework, $returnValue, '->match() should return $this');
$this->assertTrue($response->isRedirected('/target'));
}
/** $returnValue = $framework->get('/foo', function() {});
* @expectedException Symfony\Component\HttpKernel\Exception\NotFoundHttpException $this->assertSame($framework, $returnValue, '->get() should return $this');
*/
public function testMissingRoute()
{
$framework = Framework::create();
$request = Request::create('http://test.com/baz'); $returnValue = $framework->post('/foo', function() {});
$framework->handle($request); $this->assertSame($framework, $returnValue, '->post() should return $this');
}
public function testMethodRouting() $returnValue = $framework->put('/foo', function() {});
{ $this->assertSame($framework, $returnValue, '->put() should return $this');
$framework = Framework::create()
->match('/foo', function() {
return 'foo';
})
->match('/bar', function() {
return 'bar';
}, 'GET|POST')
->get('/resource', function() {
return 'get resource';
})
->post('/resource', function() {
return 'post resource';
})
->put('/resource', function() {
return 'put resource';
})
->delete('/resource', function() {
return 'delete resource';
});
$this->checkRouteResponse($framework, '/foo', 'foo'); $returnValue = $framework->delete('/foo', function() {});
$this->checkRouteResponse($framework, '/bar', 'bar'); $this->assertSame($framework, $returnValue, '->delete() should return $this');
$this->checkRouteResponse($framework, '/bar', 'bar', 'post');
$this->checkRouteResponse($framework, '/resource', 'get resource');
$this->checkRouteResponse($framework, '/resource', 'post resource', 'post');
$this->checkRouteResponse($framework, '/resource', 'put resource', 'put');
$this->checkRouteResponse($framework, '/resource', 'delete resource', 'delete');
}
protected function checkRouteResponse($framework, $path, $expectedContent, $method = 'get', $message = null) $returnValue = $framework->error(function() {});
{ $this->assertSame($framework, $returnValue, '->error() should return $this');
$request = Request::create('http://test.com' . $path, $method);
$response = $framework->handle($request);
$this->assertEquals($expectedContent, $response->getContent(), $message);
} }
} }
<?php
namespace Silex\Tests;
use Silex\Framework;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/*
* This file is part of the Silex framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.org>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* Router test cases.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.org>
*/
class RouterTest extends \PHPUnit_Framework_TestCase
{
public function testMapRouting()
{
$framework = Framework::create(array(
'/foo' => function() {
return 'foo';
},
'/bar' => function() {
return 'bar';
},
'/' => function() {
return 'root';
},
));
$this->checkRouteResponse($framework, '/foo', 'foo');
$this->checkRouteResponse($framework, '/bar', 'bar');
$this->checkRouteResponse($framework, '/', 'root');
}
public function testMapRoutingMethods()
{
$framework = Framework::create(array(
'GET /foo' => function() {
return 'foo';
},
'PUT|DELETE /bar' => function() {
return 'bar';
},
'/' => function() {
return 'root';
},
));
// foo route
$this->checkRouteResponse($framework, '/foo', 'foo');
// bar route
$this->checkRouteResponse($framework, '/bar', 'bar', 'put');
$this->checkRouteResponse($framework, '/bar', 'bar', 'delete');
// root route
$this->checkRouteResponse($framework, '/', 'root');
$this->checkRouteResponse($framework, '/', 'root', 'post');
$this->checkRouteResponse($framework, '/', 'root', 'put');
$this->checkRouteResponse($framework, '/', 'root', 'delete');
try {
$request = Request::create('http://test.com/bar');
$framework->handle($request);
$this->fail('Framework must reject HTTP GET method to /bar');
} catch (NotFoundHttpException $expected) {
}
try {
$request = Request::create('http://test.com/bar', 'post');
$framework->handle($request);
$this->fail('Framework must reject HTTP POST method to /bar');
} catch (NotFoundHttpException $expected) {
}
}
public function testMapRoutingParameters()
{
$framework = Framework::create(array(
'/hello' => function() {
return "Hello anon";
},
'/hello/:name' => function($name) {
return "Hello $name";
},
'/goodbye/:name' => function($name) {
return "Goodbye $name";
},
'/tell/:name/:message' => function($message, $name) {
return "Message for $name: $message";
},
'/' => function() {
return 'root';
},
));
$this->checkRouteResponse($framework, '/hello', 'Hello anon');
$this->checkRouteResponse($framework, '/hello/alice', 'Hello alice');
$this->checkRouteResponse($framework, '/hello/bob', 'Hello bob');
$this->checkRouteResponse($framework, '/goodbye/alice', 'Goodbye alice');
$this->checkRouteResponse($framework, '/goodbye/bob', 'Goodbye bob');
$this->checkRouteResponse($framework, '/tell/bob/secret', 'Message for bob: secret');
$this->checkRouteResponse($framework, '/', 'root');
}
public function testStatusCode()
{
$framework = Framework::create(array(
'PUT /created' => function() {
return new Response('', 201);
},
'/forbidden' => function() {
return new Response('', 403);
},
'/not_found' => function() {
return new Response('', 404);
},
));
$request = Request::create('http://test.com/created', 'put');
$response = $framework->handle($request);
$this->assertEquals(201, $response->getStatusCode());
$request = Request::create('http://test.com/forbidden');
$response = $framework->handle($request);
$this->assertEquals(403, $response->getStatusCode());
$request = Request::create('http://test.com/not_found');
$response = $framework->handle($request);
$this->assertEquals(404, $response->getStatusCode());
}
public function testRedirect()
{
$framework = Framework::create(array(
'/redirect' => function() {
$response = new Response();
$response->setRedirect('/target');
return $response;
},
));
$request = Request::create('http://test.com/redirect');
$response = $framework->handle($request);
$this->assertTrue($response->isRedirected('/target'));
}
/**
* @expectedException Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
public function testMissingRoute()
{
$framework = Framework::create();
$request = Request::create('http://test.com/baz');
$framework->handle($request);
}
public function testMethodRouting()
{
$framework = Framework::create();
$framework->match('/foo', function() {
return 'foo';
});
$framework->match('/bar', function() {
return 'bar';
}, 'GET|POST');
$framework->get('/resource', function() {
return 'get resource';
});
$framework->post('/resource', function() {
return 'post resource';
});
$framework->put('/resource', function() {
return 'put resource';
});
$framework->delete('/resource', function() {
return 'delete resource';
});
$this->checkRouteResponse($framework, '/foo', 'foo');
$this->checkRouteResponse($framework, '/bar', 'bar');
$this->checkRouteResponse($framework, '/bar', 'bar', 'post');
$this->checkRouteResponse($framework, '/resource', 'get resource');
$this->checkRouteResponse($framework, '/resource', 'post resource', 'post');
$this->checkRouteResponse($framework, '/resource', 'put resource', 'put');
$this->checkRouteResponse($framework, '/resource', 'delete resource', 'delete');
}
protected function checkRouteResponse($framework, $path, $expectedContent, $method = 'get', $message = null)
{
$request = Request::create('http://test.com' . $path, $method);
$response = $framework->handle($request);
$this->assertEquals($expectedContent, $response->getContent(), $message);
}
}
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