'E_ERROR', E_PARSE => 'E_PARSE', E_CORE_ERROR => 'E_CORE_ERROR', E_COMPILE_ERROR => 'E_COMPILE_ERROR', E_USER_ERROR => 'E_USER_ERROR', ); /** * Error store. * * @var ErrorStore */ private $store; /** * Constructor. * * @param ErrorStore $store Error store. */ public function __construct( ErrorStore $store ) { $this->store = $store; } /** * Register the shutdown handler. */ public function register(): void { register_shutdown_function( array( $this, 'on_shutdown' ) ); } /** * Shutdown handler. */ public function on_shutdown(): void { $this->record( error_get_last() ); } /** * Record an error_get_last() style array if it is fatal. * * @param array{type?: int, message?: string, file?: string, line?: int}|null $error Last error. */ public function record( ?array $error ): bool { if ( null === $error || ! isset( $error['type'] ) || ! isset( self::FATAL_TYPES[ $error['type'] ] ) ) { return false; } // Only the first line: stack traces can contain arguments with personal data. $message = strtok( (string) ( $error['message'] ?? '' ), "\n" ); try { $this->store->add( self::FATAL_TYPES[ $error['type'] ], Redactor::redact( false === $message ? '' : $message ), Redactor::redact( (string) ( $error['file'] ?? '' ) ), (int) ( $error['line'] ?? 0 ), time() ); } catch ( \Throwable $e ) { // The database may be the reason for the crash; never make shutdown worse. return false; } return true; } }