diff --git a/CHANGELOG.md b/CHANGELOG.md index 67bab9d..06f8025 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [0.9.0] - 2017-12-16 + +### Changed + +- The request handler used to generate the response must implement `Interop\Http\Server\RequestHandlerInterface`. Removed support for callables. + +### Removed + +- Removed `arguments()` option. + ## [0.8.0] - 2017-11-13 ### Changed @@ -67,6 +77,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First version +[0.9.0]: https://github.com/middlewares/error-handler/compare/v0.8.0...v0.9.0 [0.8.0]: https://github.com/middlewares/error-handler/compare/v0.7.0...v0.8.0 [0.7.0]: https://github.com/middlewares/error-handler/compare/v0.6.0...v0.7.0 [0.6.0]: https://github.com/middlewares/error-handler/compare/v0.5.0...v0.6.0 diff --git a/README.md b/README.md index a2ddb92..b84f95a 100644 --- a/README.md +++ b/README.md @@ -28,23 +28,29 @@ composer require middlewares/error-handler ## Example ```php +use Interop\Http\Server\RequestHandlerInterface; +use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -$handler = function (ServerRequestInterface $request) use ($logger) { - //Get the error info as an instance of Middlewares\HttpErrorException - $error = $request->getAttribute('error'); +class ErrorRequestHandler implements RequestHandlerInterface +{ + public function handle(ServerRequestInterface $request): ResponseInterface + { + //Get the error info as an instance of Middlewares\HttpErrorException + $error = $request->getAttribute('error'); - //The error can contains context data that you can use, for example for PSR-3 loggin - $logger->error("There's an error", $error->getContext()); + //The error can contains context data that you can use, for example for PSR-3 loggin + Logger::error("There's an error", $error->getContext()); - //Any output is captured and added to the response's body - echo $error->getMessage(); + //Any output is captured and added to the response's body + echo $error->getMessage(); - return (new Response())->withStatus($error->getCode()); -}; + return (new Response())->withStatus($error->getCode()); + } +} $dispatcher = new Dispatcher([ - new Middlewares\ErrorHandler($handler), + new Middlewares\ErrorHandler(new ErrorRequestHandler()), function ($request, $next) { $user = Session::signup($request); @@ -66,31 +72,9 @@ $response = $dispatcher->dispatch(new ServerRequest()); ## Options -#### `__construct(string|callable $handler = null)` - -Assign the callable used to handle the error. It can be a callable or a string with the format `Class::method`. The signature of the handler is the following: - -```php -use Psr\Http\Message\ServerRequestInterface; - -$handler = function (ServerRequestInterface $request) { - //Get the error info using the "error" attribute - $error = $request->getAttribute('error'); - - //Any output is captured and added to the body stream - echo $error->getMessage(); - - return (new Response())->withStatus($error->getCode()); -}; - -$dispatcher = new Dispatcher([ - new Middlewares\ErrorHandler($handler) -]); - -$response = $dispatcher->dispatch(new ServerRequest()); -``` +#### `__construct(Interop\Http\Server\RequestHandlerInterface $handler = null)` -If it's not provided, use [the default](src/ErrorHandlerDefault.php) that provides different outputs for different formats. +The request handler used to generate the response. If it's not provided, use [the default](src/ErrorHandlerDefault.php) that provides different outputs for different formats. #### `catchExceptions(true)` @@ -113,31 +97,6 @@ $dispatcher = new Dispatcher([ The attribute name used to store the instance of `Middlewares\HttpErrorException` with the error info in the server request. By default is `error`. -#### `arguments(...$args)` - -Extra arguments to pass to the error handler. This is useful to inject, for example a logger: - -```php -$handler = function (ServerRequestInterface $request, $logger) { - $error = $request->getAttribute('error'); - $message = sprintf('Oops, a "%s" erro ocurried', $error->getCode()); - - //Log the error - $logger->error($message, $error->getContext()); - - //Build the response - $response = (new Response())->withStatus($error->getCode()); - $response->getBody()->write($message); - - return $response; -}; - -$dispatcher = new Dispatcher([ - (new Middlewares\ErrorHandler($handler)) - ->arguments($logger) -]); -``` - --- Please see [CHANGELOG](CHANGELOG.md) for more information about recent changes and [CONTRIBUTING](CONTRIBUTING.md) for contributing details. diff --git a/composer.json b/composer.json index accea6b..e059950 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ }, "require": { "php": "^7.0", - "middlewares/utils": "~0.13", + "middlewares/utils": "~0.14", "http-interop/http-server-middleware": "^1.0" }, "require-dev": { diff --git a/src/ErrorHandler.php b/src/ErrorHandler.php index 49aba08..1112d24 100644 --- a/src/ErrorHandler.php +++ b/src/ErrorHandler.php @@ -5,23 +5,14 @@ use Interop\Http\Server\MiddlewareInterface; use Interop\Http\Server\RequestHandlerInterface; -use Middlewares\Utils\CallableHandler; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Throwable; class ErrorHandler implements MiddlewareInterface { - /** - * @var callable|string The handler used - */ private $handler; - /** - * @var array Extra arguments passed to the handler - */ - private $arguments = []; - /** * @var callable|null The status code validator */ @@ -37,12 +28,7 @@ class ErrorHandler implements MiddlewareInterface */ private $attribute = 'error'; - /** - * Constructor. - * - * @param callable|string|null $handler - */ - public function __construct($handler = 'Middlewares\\ErrorHandlerDefault') + public function __construct(RequestHandlerInterface $handler = null) { $this->handler = $handler; } @@ -77,16 +63,6 @@ public function attribute(string $attribute): self return $this; } - /** - * Extra arguments passed to the handler. - */ - public function arguments(...$arguments): self - { - $this->arguments = $arguments; - - return $this; - } - /** * Process a server request and return a response. */ @@ -125,7 +101,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface private function handleError(ServerRequestInterface $request, HttpErrorException $exception): ResponseInterface { $request = $request->withAttribute($this->attribute, $exception); - $handler = new CallableHandler($this->handler, $this->arguments); + $handler = $this->handler ?: new ErrorHandlerDefault(); return $handler->handle($request); } diff --git a/src/ErrorHandlerDefault.php b/src/ErrorHandlerDefault.php index 9537afd..665b98d 100644 --- a/src/ErrorHandlerDefault.php +++ b/src/ErrorHandlerDefault.php @@ -3,10 +3,11 @@ namespace Middlewares; +use Interop\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -class ErrorHandlerDefault +class ErrorHandlerDefault implements RequestHandlerInterface { private $handlers = [ 'plain' => [ @@ -37,7 +38,7 @@ class ErrorHandlerDefault /** * Execute the error handler. */ - public function __invoke(ServerRequestInterface $request): ResponseInterface + public function handle(ServerRequestInterface $request): ResponseInterface { $error = $request->getAttribute('error'); $accept = $request->getHeaderLine('Accept'); @@ -46,32 +47,32 @@ public function __invoke(ServerRequestInterface $request): ResponseInterface foreach ($this->handlers as $method => $types) { foreach ($types as $type) { if (stripos($accept, $type) !== false) { - call_user_func(__CLASS__.'::'.$method, $error); + $response->getBody()->write(call_user_func(__CLASS__.'::'.$method, $error)); return $response->withHeader('Content-Type', $type); } } } - static::html($error); + $response->getBody()->write(static::html($error)); return $response->withHeader('Content-Type', 'text/html'); } /** - * Output the error as plain text. + * Return the error as plain text. */ - public static function plain(HttpErrorException $error) + public static function plain(HttpErrorException $error): string { - echo sprintf("Error %s\n%s", $error->getCode(), $error->getMessage()); + return sprintf("Error %s\n%s", $error->getCode(), $error->getMessage()); } /** - * Output the error as svg image. + * Return the error as svg image. */ - public static function svg(HttpErrorException $error) + public static function svg(HttpErrorException $error): string { - echo << Error {$error->getCode()} @@ -81,11 +82,11 @@ public static function svg(HttpErrorException $error) } /** - * Output the error as html. + * Return the error as html. */ - public static function html(HttpErrorException $error) + public static function html(HttpErrorException $error): string { - echo << @@ -103,11 +104,11 @@ public static function html(HttpErrorException $error) } /** - * Output the error as json. + * Return the error as json. */ - public static function json(HttpErrorException $error) + public static function json(HttpErrorException $error): string { - echo json_encode([ + return json_encode([ 'error' => [ 'code' => $error->getCode(), 'message' => $error->getMessage(), @@ -116,11 +117,11 @@ public static function json(HttpErrorException $error) } /** - * Output the error as xml. + * Return the error as xml. */ - public static function xml(HttpErrorException $error) + public static function xml(HttpErrorException $error): string { - echo << {$error->getCode()} @@ -130,41 +131,33 @@ public static function xml(HttpErrorException $error) } /** - * Output the error as jpeg. + * Return the error as jpeg. */ - public static function jpeg(HttpErrorException $error) + public static function jpeg(HttpErrorException $error): string { - $image = self::createImage($error); - - imagejpeg($image); + return self::getImage($error, 'imagejpeg'); } /** - * Output the error as gif. + * Return the error as gif. */ - public static function gif(HttpErrorException $error) + public static function gif(HttpErrorException $error): string { - $image = self::createImage($error); - - imagegif($image); + return self::getImage($error, 'imagegif'); } /** - * Output the error as png. + * Return the error as png. */ - public static function png(HttpErrorException $error) + public static function png(HttpErrorException $error): string { - $image = self::createImage($error); - - imagepng($image); + return self::getImage($error, 'imagepng'); } /** - * Creates a image resource with the error text. - * - * @return resource + * Create and return a image as string. */ - private static function createImage(HttpErrorException $error) + private static function getImage(HttpErrorException $error, callable $function): string { $size = 200; $image = imagecreatetruecolor($size, $size); @@ -175,6 +168,8 @@ private static function createImage(HttpErrorException $error) imagestring($image, 5, 10, ($line * 18) + 28, $text, $textColor); } - return $image; + ob_start(); + $function($image); + return ob_get_clean(); } } diff --git a/src/HttpErrorException.php b/src/HttpErrorException.php index 2e1061a..5d3e880 100644 --- a/src/HttpErrorException.php +++ b/src/HttpErrorException.php @@ -68,7 +68,7 @@ class HttpErrorException extends Exception * * @return static */ - public static function create(int $code = 500, array $context = [], Throwable $previous = null) + public static function create(int $code = 500, array $context = [], Throwable $previous = null): self { if (!isset(self::$phrases[$code])) { throw new RuntimeException("Http error not valid ({$code})"); diff --git a/tests/ErrorHandlerTest.php b/tests/ErrorHandlerTest.php index 26e2260..8b7496b 100644 --- a/tests/ErrorHandlerTest.php +++ b/tests/ErrorHandlerTest.php @@ -5,6 +5,7 @@ use Exception; use Middlewares\ErrorHandler; use Middlewares\HttpErrorException; +use Middlewares\Utils\CallableHandler; use Middlewares\Utils\Dispatcher; use Middlewares\Utils\Factory; use PHPUnit\Framework\TestCase; @@ -14,11 +15,11 @@ class ErrorHandlerTest extends TestCase public function testError() { $response = Dispatcher::run([ - new ErrorHandler(function ($request) { + new ErrorHandler(new CallableHandler(function ($request) { echo 'Page not found'; return Factory::createResponse($request->getAttribute('error')->getCode()); - }), + })), function () { return Factory::createResponse(404); }, @@ -31,7 +32,7 @@ function () { public function testHttpErrorException() { $response = Dispatcher::run([ - new ErrorHandler(function ($request) { + new ErrorHandler(new CallableHandler(function ($request) { $error = $request->getAttribute('error'); echo $error->getCode(); @@ -39,7 +40,7 @@ public function testHttpErrorException() echo '-'.$error->getContext()['foo']; return Factory::createResponse($error->getCode()); - }), + })), function () { throw HttpErrorException::create(500, ['foo' => 'bar']); }, @@ -52,11 +53,11 @@ function () { public function testAttribute() { $response = Dispatcher::run([ - (new ErrorHandler(function ($request) { + (new ErrorHandler(new CallableHandler(function ($request) { echo 'Page not found'; return Factory::createResponse($request->getAttribute('foo')->getCode()); - }))->attribute('foo'), + })))->attribute('foo'), function () { return Factory::createResponse(404); }, @@ -71,11 +72,11 @@ public function testException() $exception = new Exception('Error Processing Request'); $response = Dispatcher::run([ - (new ErrorHandler(function ($request) { + (new ErrorHandler(new CallableHandler(function ($request) { echo $request->getAttribute('error')->getPrevious(); return Factory::createResponse($request->getAttribute('error')->getCode()); - }))->catchExceptions(), + })))->catchExceptions(), function ($request) use ($exception) { echo 'not showed text'; throw $exception; @@ -133,23 +134,6 @@ function () { $this->assertEquals('text/html', $response->getHeaderLine('Content-Type')); } - public function testArguments() - { - $response = Dispatcher::run([ - (new ErrorHandler(function ($request, $message) { - echo $message; - - return Factory::createResponse($request->getAttribute('error')->getCode()); - }))->arguments('Hello world'), - function () { - return Factory::createResponse(500); - }, - ]); - - $this->assertEquals(500, $response->getStatusCode()); - $this->assertEquals('Hello world', (string) $response->getBody()); - } - public function testValidators() { $validator = function ($code) {