mirror of
https://github.com/danog/amp.git
synced 2024-11-30 04:29:08 +01:00
Remove promise error handler
Exceptions thrown from when callbacks are now forwarded directly to the loop error handler.
This commit is contained in:
parent
be34c49a2d
commit
754a29e86c
@ -2,8 +2,6 @@
|
||||
|
||||
namespace Amp;
|
||||
|
||||
use Amp\Promise\ErrorHandler;
|
||||
|
||||
/**
|
||||
* Creates a failed stream (which is also a promise) using the given exception.
|
||||
*/
|
||||
@ -25,7 +23,9 @@ final class Failure implements Stream {
|
||||
try {
|
||||
$onResolved($this->exception, null);
|
||||
} catch (\Throwable $exception) {
|
||||
ErrorHandler::notify($exception);
|
||||
Loop::defer(function () use ($exception) {
|
||||
throw $exception;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,8 @@
|
||||
namespace Amp\Internal;
|
||||
|
||||
use Amp\Failure;
|
||||
use Amp\Loop;
|
||||
use Amp\Promise;
|
||||
use Amp\Promise\ErrorHandler;
|
||||
|
||||
/**
|
||||
* Trait used by Promise implementations. Do not use this trait in your code, instead compose your class from one of
|
||||
@ -35,7 +35,9 @@ trait Placeholder {
|
||||
try {
|
||||
$onResolved(null, $this->result);
|
||||
} catch (\Throwable $exception) {
|
||||
ErrorHandler::notify($exception);
|
||||
Loop::defer(function () use ($exception) {
|
||||
throw $exception;
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -80,7 +82,9 @@ trait Placeholder {
|
||||
try {
|
||||
$onResolved(null, $this->result);
|
||||
} catch (\Throwable $exception) {
|
||||
ErrorHandler::notify($exception);
|
||||
Loop::defer(function () use ($exception) {
|
||||
throw $exception;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,8 @@
|
||||
namespace Amp\Internal;
|
||||
|
||||
use Amp\Deferred;
|
||||
use Amp\Loop;
|
||||
use Amp\Promise;
|
||||
use Amp\Promise\ErrorHandler;
|
||||
use Amp\Success;
|
||||
|
||||
/**
|
||||
@ -80,7 +80,9 @@ trait Producer {
|
||||
$promises[] = $result;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
ErrorHandler::notify($e);
|
||||
Loop::defer(function () use ($e) {
|
||||
throw $e;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +94,9 @@ trait Producer {
|
||||
$count = \count($promises);
|
||||
$f = static function ($e) use ($deferred, $value, &$count) {
|
||||
if ($e) {
|
||||
ErrorHandler::notify($e);
|
||||
Loop::defer(function () use ($e) {
|
||||
throw $e;
|
||||
});
|
||||
}
|
||||
if (!--$count) {
|
||||
$deferred->resolve($value);
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Amp\Internal;
|
||||
|
||||
use Amp\Promise\ErrorHandler;
|
||||
use Amp\Loop;
|
||||
|
||||
/**
|
||||
* Stores a set of functions to be invoked when a promise is resolved.
|
||||
@ -47,7 +47,9 @@ class WhenQueue {
|
||||
try {
|
||||
$callback($exception, $value);
|
||||
} catch (\Throwable $exception) {
|
||||
ErrorHandler::notify($exception);
|
||||
Loop::defer(function () use ($exception) {
|
||||
throw $exception;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
15
lib/Loop.php
15
lib/Loop.php
@ -6,7 +6,6 @@ use Amp\Loop\Driver;
|
||||
use Amp\Loop\Factory;
|
||||
use Amp\Loop\InvalidWatcherException;
|
||||
use Amp\Loop\UnsupportedFeatureException;
|
||||
use Amp\Promise\ErrorHandler;
|
||||
|
||||
/**
|
||||
* Accessor to allow global access to the event loop.
|
||||
@ -51,23 +50,9 @@ final class Loop {
|
||||
self::$driver->defer(wrap($callback));
|
||||
}
|
||||
|
||||
self::setupErrorHandlerIfNoneExists();
|
||||
self::$driver->run();
|
||||
}
|
||||
|
||||
private static function setupErrorHandlerIfNoneExists() {
|
||||
$errorHandler = ErrorHandler::set(function ($error) {
|
||||
Loop::defer(function () use ($error) {
|
||||
throw $error;
|
||||
});
|
||||
});
|
||||
|
||||
if ($errorHandler !== null) {
|
||||
// Restore if another handler has already been set
|
||||
ErrorHandler::set($errorHandler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the event loop.
|
||||
*
|
||||
|
@ -1,97 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Promise;
|
||||
|
||||
/**
|
||||
* Global error handler for promises.
|
||||
*
|
||||
* Callbacks passed to `Promise::when()` should never throw, but they might. Such errors have to be passed to this
|
||||
* global error handler to make them easily loggable. These can't be handled gracefully in any way, so we just enable
|
||||
* logging with this handler and ignore them otherwise.
|
||||
*
|
||||
* If no handler is set or that handler rethrows, it will fail hard by triggering an E_USER_ERROR leading to script
|
||||
* abortion.
|
||||
*/
|
||||
final class ErrorHandler {
|
||||
/** @var callable|null */
|
||||
private static $callback = null;
|
||||
|
||||
private function __construct() {
|
||||
// disable construction, only static helper
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new handler that will be notified on uncaught errors during promise resolution callback invocations.
|
||||
*
|
||||
* This callback can attempt to log the error or exit the execution of the script if it sees need. It receives the
|
||||
* exception as first and only parameter.
|
||||
*
|
||||
* As it's already a last chance handler, the script will be aborted using E_USER_ERROR if the handler throws. Thus
|
||||
* it's suggested to always wrap the body of your callback in a generic `try` / `catch` block, if you want to avoid
|
||||
* that.
|
||||
*
|
||||
* @param callable|null $onError Callback to invoke on errors or `null` to reset.
|
||||
*
|
||||
* @return callable|null Previous callback.
|
||||
*/
|
||||
public static function set(callable $onError = null) {
|
||||
$previous = self::$callback;
|
||||
self::$callback = $onError;
|
||||
return $previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the registered handler, that an exception occurred.
|
||||
*
|
||||
* This method MUST be called by every promise implementation if a callback passed to `Promise::when()` throws upon
|
||||
* invocation. It MUST NOT be called otherwise.
|
||||
*
|
||||
* @param \Throwable $error Exception that occurred.
|
||||
*/
|
||||
public static function notify(\Throwable $error) {
|
||||
if (self::$callback === null) {
|
||||
self::triggerErrorHandler(
|
||||
"An exception has been thrown from an Amp\\Promise::when() handler, but no handler has been registered"
|
||||
. " via Amp\\Promise\\ErrorHandler::set(). A handler has to be registered to prevent exceptions from"
|
||||
. " going unnoticed. Do NOT install an empty handler that just does nothing. If the handler is called,"
|
||||
. " there is ALWAYS something wrong.",
|
||||
$error
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
\call_user_func(self::$callback, $error);
|
||||
} catch (\Exception $e) {
|
||||
self::triggerErrorHandler(
|
||||
"An exception has been thrown from the promise error handler registered to"
|
||||
. " Amp\\Promise\\ErrorHandler::set().",
|
||||
$e
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
self::triggerErrorHandler(
|
||||
"An exception has been thrown from the promise error handler registered to"
|
||||
. " Amp\\Promise\\ErrorHandler::set().",
|
||||
$e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static function triggerErrorHandler($message, $error) {
|
||||
// We're already a last chance handler, throwing doesn't make sense, so use E_USER_ERROR.
|
||||
// E_USER_ERROR is recoverable by a handler set via set_error_handler, which might throw, too.
|
||||
|
||||
$message .= "\n\n" . (string) $error;
|
||||
|
||||
try {
|
||||
\trigger_error($message, E_USER_ERROR);
|
||||
} catch (\Exception $e) {
|
||||
\set_error_handler(null);
|
||||
\trigger_error($message, E_USER_ERROR);
|
||||
} catch (\Throwable $e) {
|
||||
\set_error_handler(null);
|
||||
\trigger_error($message, E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,8 +2,6 @@
|
||||
|
||||
namespace Amp;
|
||||
|
||||
use Amp\Promise\ErrorHandler;
|
||||
|
||||
/**
|
||||
* Creates a successful stream (which is also a promise) using the given value (which can be any value except another
|
||||
* object implementing \Amp\Promise).
|
||||
@ -32,7 +30,9 @@ final class Success implements Stream {
|
||||
try {
|
||||
$onResolved(null, $this->value);
|
||||
} catch (\Throwable $exception) {
|
||||
ErrorHandler::notify($exception);
|
||||
Loop::defer(function () use ($exception) {
|
||||
throw $exception;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user