Skip to content

Commit 9767e69

Browse files
ostroluckysebastianbergmann
authored andcommitted
Guard against recursive previous error handler forwarding
1 parent 8f5180f commit 9767e69

2 files changed

Lines changed: 50 additions & 5 deletions

File tree

src/Runner/ErrorHandler.php

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ final class ErrorHandler
7474
private ExcludeList $excludeList;
7575
private bool $enabled = false;
7676
private ?int $originalErrorReportingLevel = null;
77+
private bool $isForwarding = false;
7778

7879
/**
7980
* @var ?callable
@@ -291,8 +292,14 @@ public function handleNonTestCaseIssue(int $errorNumber, string $errorString, st
291292

292293
if ($errorString === '' || $errorFile === '' || $errorLine < 1) {
293294
// @codeCoverageIgnoreStart
294-
if ($this->previousNonTestCaseErrorHandler !== null) {
295-
($this->previousNonTestCaseErrorHandler)($errorNumber, $errorString, $errorFile, $errorLine);
295+
if ($this->previousNonTestCaseErrorHandler !== null && !$this->isForwarding) {
296+
$this->isForwarding = true;
297+
298+
try {
299+
($this->previousNonTestCaseErrorHandler)($errorNumber, $errorString, $errorFile, $errorLine);
300+
} finally {
301+
$this->isForwarding = false;
302+
}
296303
}
297304

298305
return true;
@@ -306,8 +313,14 @@ public function handleNonTestCaseIssue(int $errorNumber, string $errorString, st
306313
*
307314
* @see https://github.com/sebastianbergmann/phpunit/issues/6817
308315
*/
309-
if ($this->previousNonTestCaseErrorHandler !== null) {
310-
($this->previousNonTestCaseErrorHandler)($errorNumber, $errorString, $errorFile, $errorLine);
316+
if ($this->previousNonTestCaseErrorHandler !== null && !$this->isForwarding) {
317+
$this->isForwarding = true;
318+
319+
try {
320+
($this->previousNonTestCaseErrorHandler)($errorNumber, $errorString, $errorFile, $errorLine);
321+
} finally {
322+
$this->isForwarding = false;
323+
}
311324
}
312325

313326
/**
@@ -1002,7 +1015,7 @@ private function deprecationIgnoredByTest(TestMethod $test, string $message): bo
10021015

10031016
private function forwardToPreviousErrorHandler(int $errorNumber, string $errorString, string $errorFile, int $errorLine): bool
10041017
{
1005-
if ($this->previousErrorHandler === null) {
1018+
if ($this->previousErrorHandler === null || $this->isForwarding) {
10061019
return false;
10071020
}
10081021

@@ -1026,9 +1039,13 @@ private function forwardToPreviousErrorHandler(int $errorNumber, string $errorSt
10261039
$restoreRequired = true;
10271040
}
10281041

1042+
$this->isForwarding = true;
1043+
10291044
try {
10301045
return (bool) ($this->previousErrorHandler)($errorNumber, $errorString, $errorFile, $errorLine);
10311046
} finally {
1047+
$this->isForwarding = false;
1048+
10321049
if ($restoreRequired) {
10331050
error_reporting($errorReportingLevel);
10341051
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
PHPUnit does not enter an infinite loop when previous error handler calls it back
3+
--FILE--
4+
<?php declare(strict_types=1);
5+
$_SERVER['argv'][] = '--do-not-cache-result';
6+
$_SERVER['argv'][] = '--no-configuration';
7+
$_SERVER['argv'][] = __DIR__ . '/_files/PreviousErrorHandlerTest.php';
8+
9+
require __DIR__ . '/../../bootstrap.php';
10+
11+
use PHPUnit\Runner\ErrorHandler;
12+
13+
set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline): bool {
14+
return ErrorHandler::instance()->__invoke($errno, $errstr, $errfile, $errline);
15+
});
16+
17+
(new PHPUnit\TextUI\Application)->run($_SERVER['argv']);
18+
--EXPECTF--
19+
PHPUnit %s by Sebastian Bergmann and contributors.
20+
21+
Runtime: PHP %s
22+
23+
N 1 / 1 (100%)
24+
25+
Time: %s, Memory: %s
26+
27+
OK, but there were issues!
28+
Tests: 1, Assertions: 1, Notices: 1.

0 commit comments

Comments
 (0)