site = $site; } public function listen() { $runner = new RequestHandlerRunner( $this->safelyBootAndGetHandler(), new SapiEmitter, [ServerRequestFactory::class, 'fromGlobals'], function (Throwable $e) { $generator = new ErrorResponseGenerator; return $generator($e, new ServerRequest, new Response); } ); $runner->run(); } /** * Try to boot Flarum, and retrieve the app's HTTP request handler. * * We catch all exceptions happening during this process and format them to * prevent exposure of sensitive information. * * @return \Psr\Http\Server\RequestHandlerInterface */ private function safelyBootAndGetHandler() { try { return $this->site->bootApp()->getRequestHandler(); } catch (Throwable $e) { // Apply response code first so whatever happens, it's set before anything is printed http_response_code(500); try { $this->cleanBootExceptionLog($e); } catch (Throwable $e) { // Ignore errors in logger. The important goal is to log the original error } $this->fallbackBootExceptionLog($e); } } /** * Attempt to log the boot exception in a clean way and stop the script execution. * This means looking for debug mode and/or our normal error logger. * There is always a risk for this to fail, * for example if the container bindings aren't present * or if there is a filesystem error. * @param Throwable $error */ private function cleanBootExceptionLog(Throwable $error) { if (app()->has('flarum.config') && app('flarum.config')->inDebugMode()) { // If the application booted far enough for the config to be available, we will check for debug mode // Since the config is loaded very early, it is very likely to be available from the container $message = $error->getMessage(); $file = $error->getFile(); $line = $error->getLine(); $type = get_class($error); echo << $message
thrown in $file on line $line
$error
ERROR; exit(1); } elseif (app()->has(LoggerInterface::class)) { // If the application booted far enough for the logger to be available, we will log the error there // Considering most boot errors are related to database or extensions, the logger should already be loaded // We check for LoggerInterface binding because it's a constructor dependency of LogReporter, // then instantiate LogReporter through the container for automatic dependency injection app(LogReporter::class)->report($error); echo 'Flarum encountered a boot error. Details have been logged to the Flarum log file.'; exit(1); } } /** * If the clean logging doesn't work, then we have a last opportunity. * Here we need to be extra careful not to include anything that might be sensitive on the page. * @param Throwable $error * @throws Throwable */ private function fallbackBootExceptionLog(Throwable $error) { echo 'Flarum encountered a boot error. Details have been logged to the system PHP log file.
'; // Throwing the exception ensures it will be visible with PHP display_errors=On // but invisible if that feature is turned off // PHP will also automatically choose a valid place to log it based on the system settings throw $error; } }