This repository was archived by the owner on Jan 29, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 53
Renaming ErrorHandler to ErrorHandlerMiddleware #191
Open
boesing
wants to merge
6
commits into
zendframework:develop
Choose a base branch
from
boesing:deprecation/error-handler-to-error-middleware
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
a523e37
Added missing properties to `ErrorHandlerTest`
boesing b5bfa34
Introducing `ErrorMiddleware`
boesing aaed976
Marking `ErrorHandler` deprecated
boesing b1b6acf
`ErrorHandler` extends the `ErrorMiddleware`
boesing cf7793d
Renaming `ErrorMiddleware` to `ErrorHandlerMiddleware`
boesing 46f69fe
Renaming the `ErrorMiddlewareTest` to `ErrorHandlerMiddlewareTest`
boesing File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
<?php | ||
/** | ||
* @see https://github.com/zendframework/zend-stratigility for the canonical source repository | ||
* @copyright Copyright (c) 2016-2019 Zend Technologies USA Inc. (https://www.zend.com) | ||
* @license https://github.com/zendframework/zend-stratigility/blob/master/LICENSE.md New BSD License | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Zend\Stratigility\Middleware; | ||
|
||
use ErrorException; | ||
use Psr\Http\Message\ResponseInterface; | ||
use Psr\Http\Message\ServerRequestInterface; | ||
use Psr\Http\Server\MiddlewareInterface; | ||
use Psr\Http\Server\RequestHandlerInterface; | ||
use Throwable; | ||
use Zend\Stratigility\Exception\MissingResponseException; | ||
|
||
use function error_reporting; | ||
use function in_array; | ||
use function restore_error_handler; | ||
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: | ||
* | ||
* <code> | ||
* function ( | ||
* Throwable $e, | ||
* ServerRequestInterface $request, | ||
* ResponseInterface $response | ||
* ) : ResponseInterface | ||
* </code> | ||
* | ||
* 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: | ||
* | ||
* <code> | ||
* function ( | ||
* Throwable $e, | ||
* ServerRequestInterface $request, | ||
* ResponseInterface $response | ||
* ) : void | ||
* </code> | ||
* | ||
* 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. | ||
* | ||
* @todo Mark this error middleware as final in v4 | ||
*/ | ||
/* final */ class ErrorHandlerMiddleware 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); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.