1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-02 09:37:59 +01:00
psalm/src/Psalm/Internal/ErrorHandler.php
2022-12-18 13:20:31 -06:00

102 lines
2.7 KiB
PHP

<?php
namespace Psalm\Internal;
use RuntimeException;
use Throwable;
use function defined;
use function error_reporting;
use function fwrite;
use function implode;
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;
/**
* @internal
*/
final class ErrorHandler
{
private static bool $exceptions_enabled = true;
private static string $args = '';
/**
* @param array<int,string> $argv
*/
public static function install(array $argv = array()): void
{
self::$args = implode(' ', $argv);
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
. ' for command with CLI args "' . ErrorHandler::$args . '"',
$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);
});
}
}