1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-13 09:47:29 +01:00
psalm/src/Psalm/Internal/ErrorHandler.php
Bruce Weirdan 26978fcea1
Convert PHP errors / warnings / notices to exceptions (#5260)
* Sort uses

* Add ErrorHandler

* Use new error handler

* Drop old exception handler

* Suppress error-triggered exceptions during external autoload calls
2021-02-22 09:19:34 -05:00

91 lines
2.4 KiB
PHP

<?php
namespace Psalm\Internal;
use RuntimeException;
use Throwable;
use function defined;
use function error_reporting;
use function fwrite;
use function ini_set;
use function set_error_handler;
use function set_exception_handler;
use const E_ALL;
use const E_STRICT;
use const STDERR;
final class ErrorHandler
{
/** @var bool */
private static $exceptions_enabled = true;
public static function install(): void
{
self::setErrorReporting();
self::installErrorHandler();
self::installExceptionHandler();
}
/**
* @template T
* @param callable():T $f
* @return T
*/
public static function runWithExceptionsSuppressed(callable $f)
{
try {
self::$exceptions_enabled = false;
return $f();
} finally {
self::$exceptions_enabled = true;
}
}
/** @psalm-suppress UnusedConstructor added to prevent instantiations */
private function __construct()
{
}
private static function setErrorReporting(): void
{
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', '1');
}
private static function installErrorHandler(): void
{
set_error_handler(static function (
int $error_code,
string $error_message,
string $error_filename = 'unknown',
int $error_line = -1
): bool {
if (ErrorHandler::$exceptions_enabled && ($error_code & error_reporting())) {
throw new RuntimeException(
'PHP Error: ' . $error_message . ' in ' . $error_filename . ':' . $error_line,
$error_code
);
}
// let PHP handle suppressed errors how it sees fit
return false;
});
}
private static function installExceptionHandler(): void
{
/**
* If there is an uncaught exception,
* then print more of the backtrace than is done by default to stderr,
* then exit with a non-zero exit code to indicate failure.
*/
set_exception_handler(static function (Throwable $throwable) : void {
fwrite(STDERR, "Uncaught $throwable\n");
$version = defined('PSALM_VERSION') ? PSALM_VERSION : '(unknown version)';
fwrite(STDERR, "(Psalm $version crashed due to an uncaught Throwable)\n");
exit(1);
});
}
}