{"action":"created","issue":{"url":"https://api.github.com/repos/slimphp/Slim/issues/2222","repository_url":"https://api.github.com/repos/slimphp/Slim","labels_url":"https://api.github.com/repos/slimphp/Slim/issues/2222/labels{/name}","comments_url":"https://api.github.com/repos/slimphp/Slim/issues/2222/comments","events_url":"https://api.github.com/repos/slimphp/Slim/issues/2222/events","html_url":"https://github.com/slimphp/Slim/pull/2222","id":227624407,"number":2222,"title":"4.x Error Handling","user":{"login":"l0gicgate","id":6510935,"avatar_url":"https://avatars2.githubusercontent.com/u/6510935?v=4","gravatar_id":"","url":"https://api.github.com/users/l0gicgate","html_url":"https://github.com/l0gicgate","followers_url":"https://api.github.com/users/l0gicgate/followers","following_url":"https://api.github.com/users/l0gicgate/following{/other_user}","gists_url":"https://api.github.com/users/l0gicgate/gists{/gist_id}","starred_url":"https://api.github.com/users/l0gicgate/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/l0gicgate/subscriptions","organizations_url":"https://api.github.com/users/l0gicgate/orgs","repos_url":"https://api.github.com/users/l0gicgate/repos","events_url":"https://api.github.com/users/l0gicgate/events{/privacy}","received_events_url":"https://api.github.com/users/l0gicgate/received_events","type":"User","site_admin":false},"labels":[{"id":372378249,"url":"https://api.github.com/repos/slimphp/Slim/labels/Slim%204","name":"Slim 4","color":"fbca04","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":35,"created_at":"2017-05-10T09:48:56Z","updated_at":"2017-11-24T09:14:41Z","closed_at":null,"author_association":"NONE","pull_request":{"url":"https://api.github.com/repos/slimphp/Slim/pulls/2222","html_url":"https://github.com/slimphp/Slim/pull/2222","diff_url":"https://github.com/slimphp/Slim/pull/2222.diff","patch_url":"https://github.com/slimphp/Slim/pull/2222.patch"},"body":"**Description**\r\nAs discussed on issue #2170 I feel that the error handling has always been a bit clunky and very tightly coupled to the rendering of the error. This PR eliminates all native error handlers and replaces it with `ErrorHandler` which handles all errors. You can create a handler for any exception in these ways:\r\n\r\n```php\r\n/*\r\n * Via Shortcut Method\r\n * Notice that the callable also gets passed $displayErrorDetails optionally from the App settings\r\n * You can either pass a callback or a resolvable callable as the second parameter\r\n * My implementation uses CallableResolver when \r\n */\r\n$handler = function ($req, $res, $exception, $displayErrorDetails) {\r\n    return $response;\r\n};\r\n$app->setErrorHandler(\\Your\\Namespaced\\Exception::class, $handler);\r\n\r\n/*\r\n * Via Named Shortcut Methods Currently NotFound and NotAllowed only\r\n */\r\n$app->setNotFoundHandler(function ($request, $response, $exception) {\r\n    return $response;\r\n});\r\n\r\n/*\r\n * Via App Settings\r\n */\r\n$app = new \\Slim\\App([\r\n     /*\r\n      * The default error handler can be set using this variable\r\n      * or the $app->setDefaultErrorHandler() method\r\n      * All exceptions are caught by this handler by default which is an instance of ErrorHandler\r\n      */\r\n    'defaultErrorHandler' => function ($req, $res, $exception, $displayErrorDetails) {\r\n       return $res->withJson(['Oops...']);\r\n    },\r\n\r\n     /*\r\n      * You can also set multiple error handlers via the settings object\r\n      * by passing an associative array with 'Namespace\\Exception' => function () {}\r\n      * You can also pass a resolvable callable since this PR implements CallableResolver\r\n      */\r\n    'errorHandlers' => [\r\n       \\Slim\\Exceptions\\HttpNotFoundException::class => function (...) {...},\r\n       \\Your\\Namespaced\\Exception::class => ResolvableCallable::class\r\n    ]\r\n]);\r\n```\r\n\r\n**Error Handling/Rendering**\r\nThe rendering is finally decoupled from the handling. Everything still works the way it previously did. It will still detect the content-type and render things appropriately with the help of `ErrorRenderers`. The core `ErrorHandler` extends the `AbstractErrorHandler` class which has been completely refactored. By default it will call the appropriate `ErrorRenderer` for the supported content types. Someone can now provide their custom error renderer by extending the `AbstractErrorHandler` class and overloading the protected `renderer` variable from the parent. \r\n\r\n```php\r\nclass MyCustomErrorRenderer extends \\Slim\\Handlers\\AbstractErrorRenderer\r\n{\r\n    public function render()\r\n    {\r\n        return 'My awesome format';\r\n    }\r\n}\r\n\r\nclass MyCustomErrorHandler extends \\Slim\\Handlers\\AbstractErrorHandler\r\n{\r\n    protected $renderer = MyCustomErrorRenderer::class;\r\n}\r\n```\r\n\r\n**HTTP Exceptions**\r\nI just think it makes sense to throw HTTP based exceptions within the application. These exceptions works with nicely with the proposed native renderers. They can each have a `description` and `title` attribute as well to provide a bit more insight when the native HTML renderer is invoked. \r\n\r\nThe base class `HttpException` extends `Exception` and comes with the following sub classes :\r\n- `HttpBadGatewayException`\r\n- `HttpBadRequestException`\r\n- `HttpForbiddenException`\r\n- `HttpInternalServerErrorException`\r\n- `HttpNotAllowedException`\r\n- `HttpNotFoundException`\r\n- `HttpNotImplementedException`\r\n- `HttpUnauthorizedException`\r\n\r\nThe developer can extend the `HttpException` class if they need any other response codes that we decide not to provide with the base repository. Example if you wanted a 504 gateway timeout exception that behaves like the native ones you would do the following:\r\nI\r\n```php\r\nclass HttpForbiddenException extends HttpException\r\n{\r\n    protected $code = 504;\r\n    protected $message = 'Gateway Timeout.';\r\n    protected $title = '504 Gateway Timeout';\r\n    protected $description = 'Timed out before receiving response from the upstream server.';\r\n}\r\n```\r\n\r\n**Status**\r\n- [x] Build Passing\r\n- [x] Unit Tests (Coverage Increased +2.03%)\r\n- [ ] Discussion and Code Review."},"comment":{"url":"https://api.github.com/repos/slimphp/Slim/issues/comments/346779513","html_url":"https://github.com/slimphp/Slim/pull/2222#issuecomment-346779513","issue_url":"https://api.github.com/repos/slimphp/Slim/issues/2222","id":346779513,"user":{"login":"coveralls","id":2354108,"avatar_url":"https://avatars1.githubusercontent.com/u/2354108?v=4","gravatar_id":"","url":"https://api.github.com/users/coveralls","html_url":"https://github.com/coveralls","followers_url":"https://api.github.com/users/coveralls/followers","following_url":"https://api.github.com/users/coveralls/following{/other_user}","gists_url":"https://api.github.com/users/coveralls/gists{/gist_id}","starred_url":"https://api.github.com/users/coveralls/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/coveralls/subscriptions","organizations_url":"https://api.github.com/users/coveralls/orgs","repos_url":"https://api.github.com/users/coveralls/repos","events_url":"https://api.github.com/users/coveralls/events{/privacy}","received_events_url":"https://api.github.com/users/coveralls/received_events","type":"User","site_admin":false},"created_at":"2017-11-24T09:14:41Z","updated_at":"2017-11-24T09:14:41Z","author_association":"NONE","body":"\n[![Coverage Status](https://coveralls.io/builds/14350044/badge)](https://coveralls.io/builds/14350044)\n\nCoverage decreased (-0.1%) to 94.771% when pulling **c52842bc427deeeb81ac9156226cd6a6dad57bf6 on l0gicgate:4.x** into **6e1d77689f81ce1e383640ea1eafc1847d86cac9 on slimphp:4.x**.\n"}}
{"actor":{"display_login":"coveralls"}}