Skip to content

Commit 81516b6

Browse files
authored
Merge pull request #262 from clue-labs/unhandled-previous
Include previous exceptions when reporting unhandled promise rejections
2 parents 93d0c05 + b0668bf commit 81516b6

5 files changed

+67
-6
lines changed

src/Internal/RejectedPromise.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ public function __destruct()
3737

3838
$handler = set_rejection_handler(null);
3939
if ($handler === null) {
40-
$message = 'Unhandled promise rejection with ' . \get_class($this->reason) . ': ' . $this->reason->getMessage() . ' in ' . $this->reason->getFile() . ':' . $this->reason->getLine() . PHP_EOL;
41-
$message .= 'Stack trace:' . PHP_EOL . $this->reason->getTraceAsString();
40+
$message = 'Unhandled promise rejection with ' . $this->reason;
4241

4342
\error_log($message);
4443
return;
@@ -47,8 +46,9 @@ public function __destruct()
4746
try {
4847
$handler($this->reason);
4948
} catch (\Throwable $e) {
50-
$message = 'Fatal error: Uncaught ' . \get_class($e) . ' from unhandled promise rejection handler: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine() . PHP_EOL;
51-
$message .= 'Stack trace:' . PHP_EOL . $e->getTraceAsString();
49+
\preg_match('/^([^:\s]++)(.*+)$/sm', (string) $e, $match);
50+
\assert(isset($match[1], $match[2]));
51+
$message = 'Fatal error: Uncaught ' . $match[1] . ' from unhandled promise rejection handler' . $match[2];
5252

5353
\error_log($message);
5454
exit(255);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Calling reject() without any handlers should report unhandled rejection with all previous exceptions
3+
--INI--
4+
# suppress legacy PHPUnit 7 warning for Xdebug 3
5+
xdebug.default_enable=
6+
--FILE--
7+
<?php
8+
9+
use function React\Promise\reject;
10+
11+
require __DIR__ . '/../vendor/autoload.php';
12+
13+
reject(new RuntimeException('foo', 42, new \OverflowException('bar', 1000, new \InvalidArgumentException())));
14+
15+
?>
16+
--EXPECTF--
17+
Unhandled promise rejection with InvalidArgumentException in %s:%d
18+
Stack trace:
19+
#0 %A{main}
20+
21+
Next OverflowException: bar in %s:%d
22+
Stack trace:
23+
#0 %A{main}
24+
25+
Next RuntimeException: foo in %s:%d
26+
Stack trace:
27+
#0 %A{main}

tests/FunctionRejectTestThenMismatchThrowsTypeErrorAndShouldReportUnhandledForTypeErrorOnlyOnPhp7.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ reject(new RuntimeException('foo'))->then(null, function (UnexpectedValueExcepti
1818

1919
?>
2020
--EXPECTF--
21-
Unhandled promise rejection with TypeError: Argument 1 passed to {closure}() must be an instance of UnexpectedValueException, instance of RuntimeException given, called in %s/src/Internal/RejectedPromise.php on line %d in %s:%d
21+
Unhandled promise rejection with TypeError: Argument 1 passed to {closure}() must be an instance of UnexpectedValueException, instance of RuntimeException given, called in %s/src/Internal/RejectedPromise.php on line %d and defined in %s:%d
2222
Stack trace:
2323
#0 %s/src/Internal/RejectedPromise.php(%d): {closure}(%S)
2424
#1 %s(%d): React\Promise\Internal\RejectedPromise->then(%S)

tests/FunctionRejectTestThenMismatchThrowsTypeErrorAndShouldReportUnhandledForTypeErrorOnlyOnPhp8.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ reject(new RuntimeException('foo'))->then(null, function (UnexpectedValueExcepti
1818

1919
?>
2020
--EXPECTF--
21-
Unhandled promise rejection with TypeError: {closure%S}(): Argument #1 ($unexpected) must be of type UnexpectedValueException, RuntimeException given, called in %s/src/Internal/RejectedPromise.php on line %d in %s:%d
21+
Unhandled promise rejection with TypeError: {closure%S}(): Argument #1 ($unexpected) must be of type UnexpectedValueException, RuntimeException given, called in %s/src/Internal/RejectedPromise.php on line %d and defined in %s:%d
2222
Stack trace:
2323
#0 %s/src/Internal/RejectedPromise.php(%d): {closure%S}(%S)
2424
#1 %s(%d): React\Promise\Internal\RejectedPromise->then(%S)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
--TEST--
2+
The callback given to set_rejection_handler() should not throw an exception or the program should terminate for unhandled rejection with all previous exceptions
3+
--INI--
4+
# suppress legacy PHPUnit 7 warning for Xdebug 3
5+
xdebug.default_enable=
6+
--FILE--
7+
<?php
8+
9+
use function React\Promise\reject;
10+
use function React\Promise\set_rejection_handler;
11+
12+
require __DIR__ . '/../vendor/autoload.php';
13+
14+
set_rejection_handler(function (Throwable $e): void {
15+
throw new RuntimeException('foo', 42, new \OverflowException('bar', 1000, new \InvalidArgumentException()));
16+
});
17+
18+
reject(new RuntimeException('foo'));
19+
20+
echo 'NEVER';
21+
22+
?>
23+
--EXPECTF--
24+
Fatal error: Uncaught InvalidArgumentException from unhandled promise rejection handler in %s:%d
25+
Stack trace:
26+
#0 %A{main}
27+
28+
Next OverflowException: bar in %s:%d
29+
Stack trace:
30+
#0 %A{main}
31+
32+
Next RuntimeException: foo in %s:%d
33+
Stack trace:
34+
#0 %A{main}

0 commit comments

Comments
 (0)