summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorminstel <minstel@yandex.ru>2016-10-17 10:00:02 +0300
committerminstel <minstel@yandex.ru>2016-10-17 10:00:02 +0300
commitaf57d0a805b1abdf1bd529902d820a586b3b7455 (patch)
tree8933e67bb9bc1610a7dce96c21fa495b5cb81b5e
parentfcd9d5321f901cdbb25c2b3f0af411998b823d93 (diff)
downloadrouter-af57d0a805b1abdf1bd529902d820a586b3b7455.zip
router-af57d0a805b1abdf1bd529902d820a586b3b7455.tar.gz
router-af57d0a805b1abdf1bd529902d820a586b3b7455.tar.bz2
'Error Handler' middleware
-rw-r--r--src/Router/Middleware/ErrorHandler.php54
-rw-r--r--tests/Router/Middleware/ErrorHandlerTest.php86
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());
+ }
+}