diff options
author | minstel <minstel@yandex.ru> | 2016-10-17 10:00:02 +0300 |
---|---|---|
committer | minstel <minstel@yandex.ru> | 2016-10-17 10:00:02 +0300 |
commit | af57d0a805b1abdf1bd529902d820a586b3b7455 (patch) | |
tree | 8933e67bb9bc1610a7dce96c21fa495b5cb81b5e | |
parent | fcd9d5321f901cdbb25c2b3f0af411998b823d93 (diff) | |
download | router-af57d0a805b1abdf1bd529902d820a586b3b7455.zip router-af57d0a805b1abdf1bd529902d820a586b3b7455.tar.gz router-af57d0a805b1abdf1bd529902d820a586b3b7455.tar.bz2 |
'Error Handler' middleware
-rw-r--r-- | src/Router/Middleware/ErrorHandler.php | 54 | ||||
-rw-r--r-- | tests/Router/Middleware/ErrorHandlerTest.php | 86 |
2 files changed, 140 insertions, 0 deletions
diff --git a/src/Router/Middleware/ErrorHandler.php b/src/Router/Middleware/ErrorHandler.php new file mode 100644 index 0000000..789c455 --- /dev/null +++ b/src/Router/Middleware/ErrorHandler.php @@ -0,0 +1,54 @@ +<?php + +namespace Jasny\Router\Middleware; + +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Handle error in following middlewares/app actions + */ +class ErrorHandler +{ + /** + * Run middleware action + * + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @param callback $next + * @return ResponseInterface + */ + public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null) + { + if ($next && !is_callable($next)) { + throw new \InvalidArgumentException("'next' should be a callback"); + } + + $error = false; + + try { + $response = $next ? call_user_func($next, $request, $response) : $response; + } catch(\Throwable $e) { + $error = true; + } catch(\Exception $e) { #This block can be removed when migrating to PHP7, because Throwable represents both Exception and Error + $error = true; + } + + return $error ? $this->handleError($response) : $response; + } + + /** + * Handle caught error + * + * @param ResponseInterface $response + * @return ResponseInterface + */ + protected function handleError($response) + { + $body = $response->getBody(); + $body->rewind(); + $body->write('Unexpected error'); + + return $response->withStatus(500, 'Internal Server Error')->withBody($body); + } +} diff --git a/tests/Router/Middleware/ErrorHandlerTest.php b/tests/Router/Middleware/ErrorHandlerTest.php new file mode 100644 index 0000000..c4b6313 --- /dev/null +++ b/tests/Router/Middleware/ErrorHandlerTest.php @@ -0,0 +1,86 @@ +<?php + +use Jasny\Router\Middleware\ErrorHandler; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamInterface; + +class ErrorHandlerTest extends PHPUnit_Framework_TestCase +{ + /** + * Test invoke with invalid 'next' param + */ + public function testInvokeInvalidNext() + { + $middleware = new ErrorHandler(); + list($request, $response) = $this->getRequests(); + + $this->expectException(\InvalidArgumentException::class); + + $result = $middleware($request, $response, 'not_callable'); + } + + /** + * Test that exception in 'next' callback is caught + */ + public function testInvokeCatchError() + { + $middleware = new ErrorHandler(); + list($request, $response) = $this->getRequests(); + + $this->expectCatchError($response); + + $result = $middleware($request, $response, function($request, $response) { + throw new Exception('Test exception'); + }); + + $this->assertEquals(get_class($response), get_class($result), "Middleware should return response object"); + } + + /** + * Test case when there is no error + */ + public function testInvokeNoError() + { + $middleware = new ErrorHandler(); + list($request, $response) = $this->getRequests(); + + $result = $middleware($request, $response, function($request, $response) { + $response->nextCalled = true; + + return $response; + }); + + $this->assertEquals(get_class($response), get_class($result), "Middleware should return response object"); + $this->assertTrue($result->nextCalled, "'next' was not called"); + } + + /** + * Get requests for testing + * + * @return array + */ + public function getRequests() + { + $request = $this->createMock(ServerRequestInterface::class); + $response = $this->createMock(ResponseInterface::class); + + return [$request, $response]; + } + + /** + * Expect for error + * + * @param ResponseInterface $response + */ + public function expectCatchError($response) + { + $stream = $this->createMock(StreamInterface::class); + $stream->expects($this->once())->method('rewind'); + $stream->expects($this->once())->method('write')->with($this->equalTo('Unexpected error')); + + $response->method('getBody')->will($this->returnValue($stream)); + $response->expects($this->once())->method('withBody')->with($this->equalTo($stream))->will($this->returnSelf()); + $response->expects($this->once())->method('withStatus')->with($this->equalTo(500), $this->equalTo('Internal Server Error'))->will($this->returnSelf()); + } +} |