diff --git a/src/Framework/Error/Deprecation.php b/src/Framework/Error/Deprecation.php index 1a501ebeea5..f3cff80e948 100644 --- a/src/Framework/Error/Deprecation.php +++ b/src/Framework/Error/Deprecation.php @@ -14,5 +14,4 @@ */ final class Deprecation extends Error { - public static $enabled = true; } diff --git a/src/Framework/Error/Notice.php b/src/Framework/Error/Notice.php index 309af987d0b..71e75bfd8af 100644 --- a/src/Framework/Error/Notice.php +++ b/src/Framework/Error/Notice.php @@ -14,5 +14,4 @@ */ final class Notice extends Error { - public static $enabled = true; } diff --git a/src/Framework/Error/Warning.php b/src/Framework/Error/Warning.php index b53493dfadf..a2a1bf5fb0d 100644 --- a/src/Framework/Error/Warning.php +++ b/src/Framework/Error/Warning.php @@ -14,5 +14,4 @@ */ final class Warning extends Error { - public static $enabled = true; } diff --git a/src/Framework/TestResult.php b/src/Framework/TestResult.php index 9b719c3e075..97cba51a8ab 100644 --- a/src/Framework/TestResult.php +++ b/src/Framework/TestResult.php @@ -97,11 +97,31 @@ final class TestResult implements Countable */ private $codeCoverage; + /** + * @var bool + */ + private $registerErrorHandler = true; + + /** + * @var bool + */ + private $convertDeprecationsToExceptions = true; + /** * @var bool */ private $convertErrorsToExceptions = true; + /** + * @var bool + */ + private $convertNoticesToExceptions = true; + + /** + * @var bool + */ + private $convertWarningsToExceptions = true; + /** * @var bool */ @@ -612,19 +632,15 @@ public function run(Test $test): void $this->startTest($test); - $errorHandlerSet = false; - - if ($this->convertErrorsToExceptions) { - $oldErrorHandler = \set_error_handler( - [ErrorHandler::class, 'handleError'], - \E_ALL | \E_STRICT + if ($this->registerErrorHandler && ($this->convertDeprecationsToExceptions || $this->convertErrorsToExceptions || $this->convertNoticesToExceptions || $this->convertWarningsToExceptions)) { + $errorHandler = new ErrorHandler( + $this->convertDeprecationsToExceptions, + $this->convertErrorsToExceptions, + $this->convertNoticesToExceptions, + $this->convertWarningsToExceptions ); - if ($oldErrorHandler === null) { - $errorHandlerSet = true; - } else { - \restore_error_handler(); - } + $errorHandler->register(); } $collectCodeCoverage = $this->codeCoverage !== null && @@ -831,8 +847,10 @@ public function run(Test $test): void } } - if ($errorHandlerSet) { - \restore_error_handler(); + if (isset($errorHandler)) { + $errorHandler->unregister(); + + unset($errorHandler); } if ($error) { @@ -960,6 +978,38 @@ public function setCodeCoverage(CodeCoverage $codeCoverage): void $this->codeCoverage = $codeCoverage; } + /** + * Enables or disables the registration of PHPUnit's error handler. + */ + public function registerErrorHandler(bool $flag): void + { + $this->registerErrorHandler = $flag; + } + + /** + * Returns whether PHPUnit's error handler is to be registered. + */ + public function getRegisterErrorHandler(): bool + { + return $this->registerErrorHandler; + } + + /** + * Enables or disables the deprecation-to-exception conversion. + */ + public function convertDeprecationsToExceptions(bool $flag): void + { + $this->convertDeprecationsToExceptions = $flag; + } + + /** + * Returns the deprecation-to-exception conversion setting. + */ + public function getConvertDeprecationsToExceptions(): bool + { + return $this->convertDeprecationsToExceptions; + } + /** * Enables or disables the error-to-exception conversion. */ @@ -976,6 +1026,38 @@ public function getConvertErrorsToExceptions(): bool return $this->convertErrorsToExceptions; } + /** + * Enables or disables the notice-to-exception conversion. + */ + public function convertNoticesToExceptions(bool $flag): void + { + $this->convertNoticesToExceptions = $flag; + } + + /** + * Returns the notice-to-exception conversion setting. + */ + public function getConvertNoticesToExceptions(): bool + { + return $this->convertNoticesToExceptions; + } + + /** + * Enables or disables the warning-to-exception conversion. + */ + public function convertWarningsToExceptions(bool $flag): void + { + $this->convertWarningsToExceptions = $flag; + } + + /** + * Returns the warning-to-exception conversion setting. + */ + public function getConvertWarningsToExceptions(): bool + { + return $this->convertWarningsToExceptions; + } + /** * Enables or disables the stopping when an error occurs. */ diff --git a/src/TextUI/TestRunner.php b/src/TextUI/TestRunner.php index c645c6f09ba..8372d2c23e3 100644 --- a/src/TextUI/TestRunner.php +++ b/src/TextUI/TestRunner.php @@ -9,9 +9,6 @@ */ namespace PHPUnit\TextUI; -use PHPUnit\Framework\Error\Deprecation; -use PHPUnit\Framework\Error\Notice; -use PHPUnit\Framework\Error\Warning; use PHPUnit\Framework\Exception; use PHPUnit\Framework\Test; use PHPUnit\Framework\TestCase; @@ -223,20 +220,20 @@ public function doRun(Test $suite, array $arguments = [], bool $exit = true): Te unset($listener, $listenerNeeded); - if (!$arguments['convertErrorsToExceptions']) { - $result->convertErrorsToExceptions(false); + if (!$arguments['convertDeprecationsToExceptions']) { + $result->convertDeprecationsToExceptions(false); } - if (!$arguments['convertDeprecationsToExceptions']) { - Deprecation::$enabled = false; + if (!$arguments['convertErrorsToExceptions']) { + $result->convertErrorsToExceptions(false); } if (!$arguments['convertNoticesToExceptions']) { - Notice::$enabled = false; + $result->convertNoticesToExceptions(false); } if (!$arguments['convertWarningsToExceptions']) { - Warning::$enabled = false; + $result->convertWarningsToExceptions(false); } if ($arguments['stopOnError']) { diff --git a/src/Util/ErrorHandler.php b/src/Util/ErrorHandler.php index 871a44b4077..b54dfab722a 100644 --- a/src/Util/ErrorHandler.php +++ b/src/Util/ErrorHandler.php @@ -14,99 +14,101 @@ use PHPUnit\Framework\Error\Notice; use PHPUnit\Framework\Error\Warning; -/** - * @internal This class is not covered by the backward compatibility promise for PHPUnit - */ final class ErrorHandler { - private static $errorStack = []; + /** + * @var bool + */ + private $convertDeprecationsToExceptions; /** - * Returns the error stack. + * @var bool */ - public static function getErrorStack(): array - { - return self::$errorStack; - } + private $convertErrorsToExceptions; /** - * @throws \PHPUnit\Framework\Error\Deprecation - * @throws \PHPUnit\Framework\Error\Error - * @throws \PHPUnit\Framework\Error\Notice - * @throws \PHPUnit\Framework\Error\Warning + * @var bool */ - public static function handleError(int $errorNumber, string $errorString, string $errorFile, int $errorLine): bool + private $convertNoticesToExceptions; + + /** + * @var bool + */ + private $convertWarningsToExceptions; + + /** + * @var bool + */ + private $registered = false; + + public function __construct(bool $convertDeprecationsToExceptions, bool $convertErrorsToExceptions, bool $convertNoticesToExceptions, bool $convertWarningsToExceptions) { - if (!($errorNumber & \error_reporting())) { - return false; - } + $this->convertDeprecationsToExceptions = $convertDeprecationsToExceptions; + $this->convertErrorsToExceptions = $convertErrorsToExceptions; + $this->convertNoticesToExceptions = $convertNoticesToExceptions; + $this->convertWarningsToExceptions = $convertWarningsToExceptions; + } - self::$errorStack[] = [$errorNumber, $errorString, $errorFile, $errorLine]; + public function __invoke(int $errorNumber, string $errorString, string $errorFile, int $errorLine): bool + { + switch ($errorNumber) { + case \E_NOTICE: + case \E_USER_NOTICE: + case \E_STRICT: + if (!$this->convertNoticesToExceptions) { + return false; + } - $trace = \debug_backtrace(); - \array_shift($trace); + throw new Notice($errorString, $errorNumber, $errorFile, $errorLine); - foreach ($trace as $frame) { - if ($frame['function'] === '__toString') { - return false; - } - } + case \E_WARNING: + case \E_USER_WARNING: + if (!$this->convertWarningsToExceptions) { + return false; + } - if ($errorNumber === \E_NOTICE || $errorNumber === \E_USER_NOTICE || $errorNumber === \E_STRICT) { - if (!Notice::$enabled) { - return false; - } - - $exception = Notice::class; - } elseif ($errorNumber === \E_WARNING || $errorNumber === \E_USER_WARNING) { - if (!Warning::$enabled) { - return false; - } - - $exception = Warning::class; - } elseif ($errorNumber === \E_DEPRECATED || $errorNumber === \E_USER_DEPRECATED) { - if (!Deprecation::$enabled) { - return false; - } - - $exception = Deprecation::class; - } else { - $exception = Error::class; - } + throw new Warning($errorString, $errorNumber, $errorFile, $errorLine); + + case \E_DEPRECATED: + case \E_USER_DEPRECATED: + if (!$this->convertDeprecationsToExceptions) { + return false; + } + + throw new Deprecation($errorString, $errorNumber, $errorFile, $errorLine); - throw new $exception($errorString, $errorNumber, $errorFile, $errorLine); + default: + if (!$this->convertErrorsToExceptions) { + return false; + } + + throw new Error($errorString, $errorNumber, $errorFile, $errorLine); + } } - /** - * Registers an error handler and returns a function that will restore - * the previous handler when invoked - * - * @param int $severity PHP predefined error constant - * - * @throws \Exception if event of specified severity is emitted - */ - public static function handleErrorOnce($severity = \E_WARNING): callable + public function register(): void { - $terminator = function () { - static $expired = false; + if ($this->registered) { + return; + } - if (!$expired) { - $expired = true; + $oldErrorHandler = \set_error_handler($this); - return \restore_error_handler(); - } - }; + if ($oldErrorHandler !== null) { + \restore_error_handler(); - \set_error_handler( - function ($errorNumber, $errorString) use ($severity) { - if ($errorNumber === $severity) { - return; - } + return; + } - return false; - } - ); + $this->registered = true; + } + + public function unregister(): void + { + if (!$this->registered) { + return; + } - return $terminator; + \restore_error_handler(); } } diff --git a/src/Util/RegularExpression.php b/src/Util/RegularExpression.php index 303f4b287ab..d0e05b2e092 100644 --- a/src/Util/RegularExpression.php +++ b/src/Util/RegularExpression.php @@ -15,15 +15,24 @@ final class RegularExpression { /** - * @throws \Exception - * * @return false|int */ public static function safeMatch(string $pattern, string $subject, ?array $matches = null, int $flags = 0, int $offset = 0) { - $handler_terminator = ErrorHandler::handleErrorOnce(); - $match = \preg_match($pattern, $subject, $matches, $flags, $offset); - $handler_terminator(); + \set_error_handler( + function ($errorNumber, $errorString) + { + if ($errorNumber === \E_WARNING) { + return; + } + + return false; + } + ); + + $match = \preg_match($pattern, $subject, $matches, $flags, $offset); + + \restore_error_handler(); return $match; }