From a523e3727fa22867b0c82e0d381edd602aaf82e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Mon, 19 Aug 2019 14:02:27 +0200 Subject: [PATCH 1/6] Added missing properties to `ErrorHandlerTest` --- test/Middleware/ErrorHandlerTest.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/Middleware/ErrorHandlerTest.php b/test/Middleware/ErrorHandlerTest.php index 8900584..ddf3015 100644 --- a/test/Middleware/ErrorHandlerTest.php +++ b/test/Middleware/ErrorHandlerTest.php @@ -34,6 +34,26 @@ class ErrorHandlerTest extends TestCase /** @var callable */ private $responseFactory; + /** + * @var ObjectProphecy|ServerRequestInterface + */ + private $request; + + /** + * @var ObjectProphecy|StreamInterface + */ + private $body; + + /** + * @var ObjectProphecy|RequestHandlerInterface + */ + private $handler; + + /** + * @var int + */ + private $errorReporting; + public function setUp() { $this->response = $this->prophesize(ResponseInterface::class); From b5bfa34450190640a5c41296f1027e8bac302e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Mon, 19 Aug 2019 14:04:37 +0200 Subject: [PATCH 2/6] Introducing `ErrorMiddleware` The `ErrorMiddleware` is just a drop-in replacement for the `ErrorHandler` to solve a naming conflict. As the `ErrorHandler` is implementing the `MiddlewareInterface`, it supposed to be called `ErrorMiddleware` instead. --- src/Middleware/ErrorMiddleware.php | 200 ++++++++++++++++++ test/Middleware/ErrorMiddlewareTest.php | 259 ++++++++++++++++++++++++ 2 files changed, 459 insertions(+) create mode 100644 src/Middleware/ErrorMiddleware.php create mode 100644 test/Middleware/ErrorMiddlewareTest.php diff --git a/src/Middleware/ErrorMiddleware.php b/src/Middleware/ErrorMiddleware.php new file mode 100644 index 0000000..a484c12 --- /dev/null +++ b/src/Middleware/ErrorMiddleware.php @@ -0,0 +1,200 @@ + + * function ( + * Throwable $e, + * ServerRequestInterface $request, + * ResponseInterface $response + * ) : ResponseInterface + * + * + * These are provided the error, and the request responsible; the response + * provided is the response prototype provided to the ErrorHandler instance + * itself, and can be used as the basis for returning an error response. + * + * An error response generator must be provided as a constructor argument; + * if not provided, an instance of Zend\Stratigility\Middleware\ErrorResponseGenerator + * will be used. + * + * Listeners use the following signature: + * + * + * function ( + * Throwable $e, + * ServerRequestInterface $request, + * ResponseInterface $response + * ) : void + * + * + * Listeners are given the error, the request responsible, and the generated + * error response, and can then react to them. They are best suited for + * logging and monitoring purposes. + * + * Listeners are attached using the attachListener() method, and triggered + * in the order attached. + */ +class ErrorMiddleware implements MiddlewareInterface +{ + /** + * @var callable[] + */ + private $listeners = []; + + /** + * @var callable Routine that will generate the error response. + */ + private $responseGenerator; + + /** + * @var callable + */ + private $responseFactory; + + /** + * @param callable $responseFactory A factory capable of returning an + * empty ResponseInterface instance to update and return when returning + * an error response. + * @param null|callable $responseGenerator Callback that will generate the final + * error response; if none is provided, ErrorResponseGenerator is used. + */ + public function __construct(callable $responseFactory, callable $responseGenerator = null) + { + $this->responseFactory = function () use ($responseFactory) : ResponseInterface { + return $responseFactory(); + }; + $this->responseGenerator = $responseGenerator ?: new ErrorResponseGenerator(); + } + + /** + * Attach an error listener. + * + * Each listener receives the following three arguments: + * + * - Throwable $error + * - ServerRequestInterface $request + * - ResponseInterface $response + * + * These instances are all immutable, and the return values of + * listeners are ignored; use listeners for reporting purposes + * only. + */ + public function attachListener(callable $listener) : void + { + if (in_array($listener, $this->listeners, true)) { + return; + } + + $this->listeners[] = $listener; + } + + /** + * Middleware to handle errors and exceptions in layers it wraps. + * + * Adds an error handler that will convert PHP errors to ErrorException + * instances. + * + * Internally, wraps the call to $next() in a try/catch block, catching + * all PHP Throwables. + * + * When an exception is caught, an appropriate error response is created + * and returned instead; otherwise, the response returned by $next is + * used. + */ + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface + { + set_error_handler($this->createErrorHandler()); + + try { + $response = $handler->handle($request); + } catch (Throwable $e) { + $response = $this->handleThrowable($e, $request); + } + + restore_error_handler(); + + return $response; + } + + /** + * Handles all throwables, generating and returning a response. + * + * Passes the error, request, and response prototype to createErrorResponse(), + * triggers all listeners with the same arguments (but using the response + * returned from createErrorResponse()), and then returns the response. + */ + private function handleThrowable(Throwable $e, ServerRequestInterface $request) : ResponseInterface + { + $generator = $this->responseGenerator; + $response = $generator($e, $request, ($this->responseFactory)()); + $this->triggerListeners($e, $request, $response); + return $response; + } + + /** + * Creates and returns a callable error handler that raises exceptions. + * + * Only raises exceptions for errors that are within the error_reporting mask. + */ + private function createErrorHandler() : callable + { + /** + * @throws ErrorException if error is not within the error_reporting mask. + */ + return function (int $errno, string $errstr, string $errfile, int $errline) : void { + if (! (error_reporting() & $errno)) { + // error_reporting does not include this error + return; + } + + throw new ErrorException($errstr, 0, $errno, $errfile, $errline); + }; + } + + /** + * Trigger all error listeners. + */ + private function triggerListeners( + Throwable $error, + ServerRequestInterface $request, + ResponseInterface $response + ) : void { + foreach ($this->listeners as $listener) { + $listener($error, $request, $response); + } + } +} diff --git a/test/Middleware/ErrorMiddlewareTest.php b/test/Middleware/ErrorMiddlewareTest.php new file mode 100644 index 0000000..552b4a2 --- /dev/null +++ b/test/Middleware/ErrorMiddlewareTest.php @@ -0,0 +1,259 @@ +response = $this->prophesize(ResponseInterface::class); + $this->responseFactory = function () { + return $this->response->reveal(); + }; + $this->request = $this->prophesize(ServerRequestInterface::class); + $this->body = $this->prophesize(StreamInterface::class); + $this->handler = $this->prophesize(RequestHandlerInterface::class); + $this->errorReporting = error_reporting(); + } + + public function tearDown() + { + error_reporting($this->errorReporting); + } + + public function createMiddleware($isDevelopmentMode = false) + { + $generator = new ErrorResponseGenerator($isDevelopmentMode); + return new ErrorMiddleware($this->responseFactory, $generator); + } + + public function testReturnsResponseFromHandlerWhenNoProblemsOccur() + { + $expectedResponse = $this->prophesize(ResponseInterface::class)->reveal(); + + $this->handler + ->handle(Argument::type(ServerRequestInterface::class)) + ->willReturn($expectedResponse); + + $this->response->withStatus(Argument::any())->shouldNotBeCalled(); + + $middleware = $this->createMiddleware(); + $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); + + $this->assertSame($expectedResponse, $result); + } + + public function testReturnsErrorResponseIfHandlerDoesNotReturnAResponse() + { + $this->handler + ->handle(Argument::type(ServerRequestInterface::class)) + ->willReturn(null); + + $this->body->write('Unknown Error')->shouldBeCalled(); + $this->response->getStatusCode()->willReturn(200); + $this->response->withStatus(500)->will([$this->response, 'reveal']); + $this->response->getReasonPhrase()->willReturn(''); + $this->response->getBody()->will([$this->body, 'reveal']); + + $middleware = $this->createMiddleware(); + $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); + + $this->assertSame($this->response->reveal(), $result); + } + + public function testReturnsErrorResponseIfHandlerRaisesAnErrorInTheErrorMask() + { + error_reporting(E_USER_DEPRECATED); + $this->handler + ->handle(Argument::type(ServerRequestInterface::class)) + ->will(function () { + trigger_error('Deprecated', E_USER_DEPRECATED); + }); + + $this->body->write('Unknown Error')->shouldBeCalled(); + $this->response->getStatusCode()->willReturn(200); + $this->response->withStatus(500)->will([$this->response, 'reveal']); + $this->response->getReasonPhrase()->willReturn(''); + $this->response->getBody()->will([$this->body, 'reveal']); + + $middleware = $this->createMiddleware(); + $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); + + $this->assertSame($this->response->reveal(), $result); + } + + public function testReturnsResponseFromHandlerWhenErrorRaisedIsNotInTheErrorMask() + { + $originalMask = error_reporting(); + error_reporting($originalMask & ~E_USER_DEPRECATED); + + $expectedResponse = $this->prophesize(ResponseInterface::class)->reveal(); + $this->handler + ->handle(Argument::type(ServerRequestInterface::class)) + ->will(function () use ($expectedResponse) { + trigger_error('Deprecated', E_USER_DEPRECATED); + return $expectedResponse; + }); + + $this->body->write('Unknown Error')->shouldNotBeCalled(); + $this->response->getStatusCode()->shouldNotBeCalled(); + $this->response->withStatus(Argument::any())->shouldNotBeCalled(); + + $middleware = $this->createMiddleware(); + $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); + + $this->assertSame($expectedResponse, $result); + } + + public function testReturnsErrorResponseIfHandlerRaisesAnException() + { + $this->handler + ->handle(Argument::type(ServerRequestInterface::class)) + ->willThrow(new RuntimeException('Exception raised', 503)); + + $this->body->write('Unknown Error')->shouldBeCalled(); + $this->response->getStatusCode()->willReturn(200); + $this->response->withStatus(503)->will([$this->response, 'reveal']); + $this->response->getReasonPhrase()->willReturn(''); + $this->response->getBody()->will([$this->body, 'reveal']); + + $middleware = $this->createMiddleware(); + $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); + + $this->assertSame($this->response->reveal(), $result); + } + + public function testResponseErrorMessageIncludesStackTraceIfDevelopmentModeIsEnabled() + { + $exception = new RuntimeException('Exception raised', 503); + $this->handler + ->handle(Argument::type(ServerRequestInterface::class)) + ->willThrow($exception); + + $this->body + ->write((new Escaper()) + ->escapeHtml((string) $exception))->shouldBeCalled(); + $this->response->getStatusCode()->willReturn(200); + $this->response->withStatus(503)->will([$this->response, 'reveal']); + $this->response->getReasonPhrase()->willReturn(''); + $this->response->getBody()->will([$this->body, 'reveal']); + + $middleware = $this->createMiddleware(true); + $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); + + $this->assertSame($this->response->reveal(), $result); + } + + public function testErrorHandlingTriggersListeners() + { + $exception = new RuntimeException('Exception raised', 503); + $this->handler + ->handle(Argument::type(ServerRequestInterface::class)) + ->willThrow($exception); + + $this->body->write('Unknown Error')->shouldBeCalled(); + $this->response->getStatusCode()->willReturn(200); + $this->response->withStatus(503)->will([$this->response, 'reveal']); + $this->response->getReasonPhrase()->willReturn(''); + $this->response->getBody()->will([$this->body, 'reveal']); + + $listener = function ($error, $request, $response) use ($exception) { + $this->assertSame($exception, $error, 'Listener did not receive same exception as was raised'); + $this->assertSame($this->request->reveal(), $request, 'Listener did not receive same request'); + $this->assertSame($this->response->reveal(), $response, 'Listener did not receive same response'); + }; + $listener2 = clone $listener; + + $middleware = $this->createMiddleware(); + $middleware->attachListener($listener); + $middleware->attachListener($listener2); + + $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); + + $this->assertSame($this->response->reveal(), $result); + } + + public function testCanProvideAlternateErrorResponseGenerator() + { + $generator = function ($e, $request, $response) { + $response = $response->withStatus(400); + $response->getBody()->write('The client messed up'); + return $response; + }; + + $this->handler + ->handle(Argument::type(ServerRequestInterface::class)) + ->willThrow(new RuntimeException('Exception raised', 503)); + + $this->response->withStatus(400)->will([$this->response, 'reveal']); + $this->response->getBody()->will([$this->body, 'reveal']); + $this->body->write('The client messed up')->shouldBeCalled(); + + $middleware = new ErrorMiddleware($this->responseFactory, $generator); + $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); + + $this->assertSame($this->response->reveal(), $result); + } + + public function testTheSameListenerIsAttachedOnlyOnce() + { + $middleware = $this->createMiddleware(); + $listener = function () { + }; + + $middleware->attachListener($listener); + $middleware->attachListener($listener); + + self::assertAttributeCount(1, 'listeners', $middleware); + } +} From aaed976d9bae8360f1769ff5c202a71cd76f6fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Mon, 19 Aug 2019 14:05:14 +0200 Subject: [PATCH 3/6] Marking `ErrorHandler` deprecated The `ErrorHandler` should be removed in v4. --- src/Middleware/ErrorHandler.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Middleware/ErrorHandler.php b/src/Middleware/ErrorHandler.php index 7588733..a959f3d 100644 --- a/src/Middleware/ErrorHandler.php +++ b/src/Middleware/ErrorHandler.php @@ -67,6 +67,8 @@ * * Listeners are attached using the attachListener() method, and triggered * in the order attached. + * + * @deprecated This class is being dropped in v4.0 in favor of the ErrorMiddleware. */ class ErrorHandler implements MiddlewareInterface { From b1b6acfb92eea9149f7b17ffb051fb145e6989c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Mon, 19 Aug 2019 14:07:30 +0200 Subject: [PATCH 4/6] `ErrorHandler` extends the `ErrorMiddleware` By just extending the `ErrorMiddleware`, we can remove the dedicated unit test and the duplicated code for the `ErrorHandler` --- src/Middleware/ErrorHandler.php | 174 +----------------- src/Middleware/ErrorMiddleware.php | 4 +- test/Middleware/ErrorHandlerTest.php | 259 --------------------------- 3 files changed, 4 insertions(+), 433 deletions(-) delete mode 100644 test/Middleware/ErrorHandlerTest.php diff --git a/src/Middleware/ErrorHandler.php b/src/Middleware/ErrorHandler.php index a959f3d..e52001f 100644 --- a/src/Middleware/ErrorHandler.php +++ b/src/Middleware/ErrorHandler.php @@ -23,180 +23,8 @@ use function set_error_handler; /** - * Error handler middleware. - * - * Use this middleware as the outermost (or close to outermost) middleware - * layer, and use it to intercept PHP errors and exceptions. - * - * The class offers two extension points: - * - * - Error response generators. - * - Listeners. - * - * Error response generators are callables with the following signature: - * - * - * function ( - * Throwable $e, - * ServerRequestInterface $request, - * ResponseInterface $response - * ) : ResponseInterface - * - * - * These are provided the error, and the request responsible; the response - * provided is the response prototype provided to the ErrorHandler instance - * itself, and can be used as the basis for returning an error response. - * - * An error response generator must be provided as a constructor argument; - * if not provided, an instance of Zend\Stratigility\Middleware\ErrorResponseGenerator - * will be used. - * - * Listeners use the following signature: - * - * - * function ( - * Throwable $e, - * ServerRequestInterface $request, - * ResponseInterface $response - * ) : void - * - * - * Listeners are given the error, the request responsible, and the generated - * error response, and can then react to them. They are best suited for - * logging and monitoring purposes. - * - * Listeners are attached using the attachListener() method, and triggered - * in the order attached. - * * @deprecated This class is being dropped in v4.0 in favor of the ErrorMiddleware. */ -class ErrorHandler implements MiddlewareInterface +class ErrorHandler extends ErrorMiddleware { - /** - * @var callable[] - */ - private $listeners = []; - - /** - * @var callable Routine that will generate the error response. - */ - private $responseGenerator; - - /** - * @var callable - */ - private $responseFactory; - - /** - * @param callable $responseFactory A factory capable of returning an - * empty ResponseInterface instance to update and return when returning - * an error response. - * @param null|callable $responseGenerator Callback that will generate the final - * error response; if none is provided, ErrorResponseGenerator is used. - */ - public function __construct(callable $responseFactory, callable $responseGenerator = null) - { - $this->responseFactory = function () use ($responseFactory) : ResponseInterface { - return $responseFactory(); - }; - $this->responseGenerator = $responseGenerator ?: new ErrorResponseGenerator(); - } - - /** - * Attach an error listener. - * - * Each listener receives the following three arguments: - * - * - Throwable $error - * - ServerRequestInterface $request - * - ResponseInterface $response - * - * These instances are all immutable, and the return values of - * listeners are ignored; use listeners for reporting purposes - * only. - */ - public function attachListener(callable $listener) : void - { - if (in_array($listener, $this->listeners, true)) { - return; - } - - $this->listeners[] = $listener; - } - - /** - * Middleware to handle errors and exceptions in layers it wraps. - * - * Adds an error handler that will convert PHP errors to ErrorException - * instances. - * - * Internally, wraps the call to $next() in a try/catch block, catching - * all PHP Throwables. - * - * When an exception is caught, an appropriate error response is created - * and returned instead; otherwise, the response returned by $next is - * used. - */ - public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface - { - set_error_handler($this->createErrorHandler()); - - try { - $response = $handler->handle($request); - } catch (Throwable $e) { - $response = $this->handleThrowable($e, $request); - } - - restore_error_handler(); - - return $response; - } - - /** - * Handles all throwables, generating and returning a response. - * - * Passes the error, request, and response prototype to createErrorResponse(), - * triggers all listeners with the same arguments (but using the response - * returned from createErrorResponse()), and then returns the response. - */ - private function handleThrowable(Throwable $e, ServerRequestInterface $request) : ResponseInterface - { - $generator = $this->responseGenerator; - $response = $generator($e, $request, ($this->responseFactory)()); - $this->triggerListeners($e, $request, $response); - return $response; - } - - /** - * Creates and returns a callable error handler that raises exceptions. - * - * Only raises exceptions for errors that are within the error_reporting mask. - */ - private function createErrorHandler() : callable - { - /** - * @throws ErrorException if error is not within the error_reporting mask. - */ - return function (int $errno, string $errstr, string $errfile, int $errline) : void { - if (! (error_reporting() & $errno)) { - // error_reporting does not include this error - return; - } - - throw new ErrorException($errstr, 0, $errno, $errfile, $errline); - }; - } - - /** - * Trigger all error listeners. - */ - private function triggerListeners( - Throwable $error, - ServerRequestInterface $request, - ResponseInterface $response - ) : void { - foreach ($this->listeners as $listener) { - $listener($error, $request, $response); - } - } } diff --git a/src/Middleware/ErrorMiddleware.php b/src/Middleware/ErrorMiddleware.php index a484c12..a2d7107 100644 --- a/src/Middleware/ErrorMiddleware.php +++ b/src/Middleware/ErrorMiddleware.php @@ -67,8 +67,10 @@ * * Listeners are attached using the attachListener() method, and triggered * in the order attached. + * + * @todo Mark this error middleware as final in v4 */ -class ErrorMiddleware implements MiddlewareInterface +/* final */ class ErrorMiddleware implements MiddlewareInterface { /** * @var callable[] diff --git a/test/Middleware/ErrorHandlerTest.php b/test/Middleware/ErrorHandlerTest.php deleted file mode 100644 index ddf3015..0000000 --- a/test/Middleware/ErrorHandlerTest.php +++ /dev/null @@ -1,259 +0,0 @@ -response = $this->prophesize(ResponseInterface::class); - $this->responseFactory = function () { - return $this->response->reveal(); - }; - $this->request = $this->prophesize(ServerRequestInterface::class); - $this->body = $this->prophesize(StreamInterface::class); - $this->handler = $this->prophesize(RequestHandlerInterface::class); - $this->errorReporting = error_reporting(); - } - - public function tearDown() - { - error_reporting($this->errorReporting); - } - - public function createMiddleware($isDevelopmentMode = false) - { - $generator = new ErrorResponseGenerator($isDevelopmentMode); - return new ErrorHandler($this->responseFactory, $generator); - } - - public function testReturnsResponseFromHandlerWhenNoProblemsOccur() - { - $expectedResponse = $this->prophesize(ResponseInterface::class)->reveal(); - - $this->handler - ->handle(Argument::type(ServerRequestInterface::class)) - ->willReturn($expectedResponse); - - $this->response->withStatus(Argument::any())->shouldNotBeCalled(); - - $middleware = $this->createMiddleware(); - $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); - - $this->assertSame($expectedResponse, $result); - } - - public function testReturnsErrorResponseIfHandlerDoesNotReturnAResponse() - { - $this->handler - ->handle(Argument::type(ServerRequestInterface::class)) - ->willReturn(null); - - $this->body->write('Unknown Error')->shouldBeCalled(); - $this->response->getStatusCode()->willReturn(200); - $this->response->withStatus(500)->will([$this->response, 'reveal']); - $this->response->getReasonPhrase()->willReturn(''); - $this->response->getBody()->will([$this->body, 'reveal']); - - $middleware = $this->createMiddleware(); - $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); - - $this->assertSame($this->response->reveal(), $result); - } - - public function testReturnsErrorResponseIfHandlerRaisesAnErrorInTheErrorMask() - { - error_reporting(E_USER_DEPRECATED); - $this->handler - ->handle(Argument::type(ServerRequestInterface::class)) - ->will(function () { - trigger_error('Deprecated', E_USER_DEPRECATED); - }); - - $this->body->write('Unknown Error')->shouldBeCalled(); - $this->response->getStatusCode()->willReturn(200); - $this->response->withStatus(500)->will([$this->response, 'reveal']); - $this->response->getReasonPhrase()->willReturn(''); - $this->response->getBody()->will([$this->body, 'reveal']); - - $middleware = $this->createMiddleware(); - $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); - - $this->assertSame($this->response->reveal(), $result); - } - - public function testReturnsResponseFromHandlerWhenErrorRaisedIsNotInTheErrorMask() - { - $originalMask = error_reporting(); - error_reporting($originalMask & ~E_USER_DEPRECATED); - - $expectedResponse = $this->prophesize(ResponseInterface::class)->reveal(); - $this->handler - ->handle(Argument::type(ServerRequestInterface::class)) - ->will(function () use ($expectedResponse) { - trigger_error('Deprecated', E_USER_DEPRECATED); - return $expectedResponse; - }); - - $this->body->write('Unknown Error')->shouldNotBeCalled(); - $this->response->getStatusCode()->shouldNotBeCalled(); - $this->response->withStatus(Argument::any())->shouldNotBeCalled(); - - $middleware = $this->createMiddleware(); - $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); - - $this->assertSame($expectedResponse, $result); - } - - public function testReturnsErrorResponseIfHandlerRaisesAnException() - { - $this->handler - ->handle(Argument::type(ServerRequestInterface::class)) - ->willThrow(new RuntimeException('Exception raised', 503)); - - $this->body->write('Unknown Error')->shouldBeCalled(); - $this->response->getStatusCode()->willReturn(200); - $this->response->withStatus(503)->will([$this->response, 'reveal']); - $this->response->getReasonPhrase()->willReturn(''); - $this->response->getBody()->will([$this->body, 'reveal']); - - $middleware = $this->createMiddleware(); - $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); - - $this->assertSame($this->response->reveal(), $result); - } - - public function testResponseErrorMessageIncludesStackTraceIfDevelopmentModeIsEnabled() - { - $exception = new RuntimeException('Exception raised', 503); - $this->handler - ->handle(Argument::type(ServerRequestInterface::class)) - ->willThrow($exception); - - $this->body - ->write((new Escaper()) - ->escapeHtml((string) $exception))->shouldBeCalled(); - $this->response->getStatusCode()->willReturn(200); - $this->response->withStatus(503)->will([$this->response, 'reveal']); - $this->response->getReasonPhrase()->willReturn(''); - $this->response->getBody()->will([$this->body, 'reveal']); - - $middleware = $this->createMiddleware(true); - $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); - - $this->assertSame($this->response->reveal(), $result); - } - - public function testErrorHandlingTriggersListeners() - { - $exception = new RuntimeException('Exception raised', 503); - $this->handler - ->handle(Argument::type(ServerRequestInterface::class)) - ->willThrow($exception); - - $this->body->write('Unknown Error')->shouldBeCalled(); - $this->response->getStatusCode()->willReturn(200); - $this->response->withStatus(503)->will([$this->response, 'reveal']); - $this->response->getReasonPhrase()->willReturn(''); - $this->response->getBody()->will([$this->body, 'reveal']); - - $listener = function ($error, $request, $response) use ($exception) { - $this->assertSame($exception, $error, 'Listener did not receive same exception as was raised'); - $this->assertSame($this->request->reveal(), $request, 'Listener did not receive same request'); - $this->assertSame($this->response->reveal(), $response, 'Listener did not receive same response'); - }; - $listener2 = clone $listener; - - $middleware = $this->createMiddleware(); - $middleware->attachListener($listener); - $middleware->attachListener($listener2); - - $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); - - $this->assertSame($this->response->reveal(), $result); - } - - public function testCanProvideAlternateErrorResponseGenerator() - { - $generator = function ($e, $request, $response) { - $response = $response->withStatus(400); - $response->getBody()->write('The client messed up'); - return $response; - }; - - $this->handler - ->handle(Argument::type(ServerRequestInterface::class)) - ->willThrow(new RuntimeException('Exception raised', 503)); - - $this->response->withStatus(400)->will([$this->response, 'reveal']); - $this->response->getBody()->will([$this->body, 'reveal']); - $this->body->write('The client messed up')->shouldBeCalled(); - - $middleware = new ErrorHandler($this->responseFactory, $generator); - $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); - - $this->assertSame($this->response->reveal(), $result); - } - - public function testTheSameListenerIsAttachedOnlyOnce() - { - $middleware = $this->createMiddleware(); - $listener = function () { - }; - - $middleware->attachListener($listener); - $middleware->attachListener($listener); - - self::assertAttributeCount(1, 'listeners', $middleware); - } -} From cf7793d9f5cabcda56d13b1442d988c00930eced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Mon, 19 Aug 2019 14:15:31 +0200 Subject: [PATCH 5/6] Renaming `ErrorMiddleware` to `ErrorHandlerMiddleware` Actually, its an error handling middleware. Renaming to `ErrorHandlerMiddleware` is more beneficial as just `ErrorMiddleware`. --- src/Middleware/ErrorHandler.php | 2 +- .../{ErrorMiddleware.php => ErrorHandlerMiddleware.php} | 2 +- test/Middleware/ErrorMiddlewareTest.php | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/Middleware/{ErrorMiddleware.php => ErrorHandlerMiddleware.php} (98%) diff --git a/src/Middleware/ErrorHandler.php b/src/Middleware/ErrorHandler.php index e52001f..853bf57 100644 --- a/src/Middleware/ErrorHandler.php +++ b/src/Middleware/ErrorHandler.php @@ -25,6 +25,6 @@ /** * @deprecated This class is being dropped in v4.0 in favor of the ErrorMiddleware. */ -class ErrorHandler extends ErrorMiddleware +class ErrorHandler extends ErrorHandlerMiddleware { } diff --git a/src/Middleware/ErrorMiddleware.php b/src/Middleware/ErrorHandlerMiddleware.php similarity index 98% rename from src/Middleware/ErrorMiddleware.php rename to src/Middleware/ErrorHandlerMiddleware.php index a2d7107..0eb0f9b 100644 --- a/src/Middleware/ErrorMiddleware.php +++ b/src/Middleware/ErrorHandlerMiddleware.php @@ -70,7 +70,7 @@ * * @todo Mark this error middleware as final in v4 */ -/* final */ class ErrorMiddleware implements MiddlewareInterface +/* final */ class ErrorHandlerMiddleware implements MiddlewareInterface { /** * @var callable[] diff --git a/test/Middleware/ErrorMiddlewareTest.php b/test/Middleware/ErrorMiddlewareTest.php index 552b4a2..cfa075a 100644 --- a/test/Middleware/ErrorMiddlewareTest.php +++ b/test/Middleware/ErrorMiddlewareTest.php @@ -18,7 +18,7 @@ use Psr\Http\Server\RequestHandlerInterface; use RuntimeException; use Zend\Escaper\Escaper; -use Zend\Stratigility\Middleware\ErrorMiddleware; +use Zend\Stratigility\Middleware\ErrorHandlerMiddleware; use Zend\Stratigility\Middleware\ErrorResponseGenerator; use function error_reporting; @@ -74,7 +74,7 @@ public function tearDown() public function createMiddleware($isDevelopmentMode = false) { $generator = new ErrorResponseGenerator($isDevelopmentMode); - return new ErrorMiddleware($this->responseFactory, $generator); + return new ErrorHandlerMiddleware($this->responseFactory, $generator); } public function testReturnsResponseFromHandlerWhenNoProblemsOccur() @@ -239,7 +239,7 @@ public function testCanProvideAlternateErrorResponseGenerator() $this->response->getBody()->will([$this->body, 'reveal']); $this->body->write('The client messed up')->shouldBeCalled(); - $middleware = new ErrorMiddleware($this->responseFactory, $generator); + $middleware = new ErrorHandlerMiddleware($this->responseFactory, $generator); $result = $middleware->process($this->request->reveal(), $this->handler->reveal()); $this->assertSame($this->response->reveal(), $result); From 46f69fe17df25e8f1f5222b6deb025b6ec2ce59f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Mon, 19 Aug 2019 14:17:37 +0200 Subject: [PATCH 6/6] Renaming the `ErrorMiddlewareTest` to `ErrorHandlerMiddlewareTest` --- .../{ErrorMiddlewareTest.php => ErrorHandlerMiddlewareTest.php} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename test/Middleware/{ErrorMiddlewareTest.php => ErrorHandlerMiddlewareTest.php} (99%) diff --git a/test/Middleware/ErrorMiddlewareTest.php b/test/Middleware/ErrorHandlerMiddlewareTest.php similarity index 99% rename from test/Middleware/ErrorMiddlewareTest.php rename to test/Middleware/ErrorHandlerMiddlewareTest.php index cfa075a..c689694 100644 --- a/test/Middleware/ErrorMiddlewareTest.php +++ b/test/Middleware/ErrorHandlerMiddlewareTest.php @@ -26,7 +26,7 @@ use const E_USER_DEPRECATED; -class ErrorMiddlewareTest extends TestCase +class ErrorHandlerMiddlewareTest extends TestCase { /** @var ResponseInterface|ObjectProphecy */ private $response;