Back to ExceptionHandler class

Method render

public static void
render
(\Throwable $error)
Render the error page based on an exception.
Parameters
  • \Throwable $error An Exception or Throwable (PHP 7+) object for which to render the error page.
Returns
  • void
Since
  • 3.0

Method render - Source code

/**
 * Render the error page based on an exception.
 *
 * @param   \Throwable  $error  An Exception or Throwable (PHP 7+) object for which to render the error page.
 *
 * @return  void
 *
 * @since   3.0
 */
public static function render(\Throwable $error)
{
    try {
        $app = Factory::getApplication();
        // Flag if we are on cli
        $isCli = $app->isClient('cli');
        // If site is offline and it's a 404 error, just go to index (to see offline message, instead of 404)
        if (!$isCli && $error->getCode() == '404' && $app->get('offline') == 1) {
            $app->redirect('index.php');
        }
        /*
         * Try and determine the format to render the error page in
         *
         * First we check if a Document instance was registered to Factory and use the type from that if available
         * If a type doesn't exist for that format, we try to use the format from the application's Input object
         * Lastly, if all else fails, we default onto the HTML format to at least render something
         */
        if (Factory::$document) {
            $format = Factory::$document->getType();
        } else {
            $format = $app->input->getString('format', 'html');
        }
        try {
            $renderer = AbstractRenderer::getRenderer($format);
        } catch (\InvalidArgumentException $e) {
            // Default to the HTML renderer
            $renderer = AbstractRenderer::getRenderer('html');
        }
        // Reset the document object in the factory, this gives us a clean slate and lets everything render properly
        Factory::$document = $renderer->getDocument();
        Factory::getApplication()->loadDocument(Factory::$document);
        $data = $renderer->render($error);
        // If nothing was rendered, just use the message from the Exception
        if (empty($data)) {
            $data = $error->getMessage();
        }
        if ($isCli) {
            echo $data;
        } else {
            /** @var CMSApplication $app */
            // Do not allow cache
            $app->allowCache(false);
            $app->setBody($data);
        }
        // This return is needed to ensure the test suite does not trigger the non-Exception handling below
        return;
    } catch (\Throwable $errorRendererError) {
        // Pass the error down
    }
    /*
     * To reach this point in the code means there was an error creating the error page.
     *
     * Let global handler to handle the error, @see bootstrap.php
     */
    if (isset($errorRendererError)) {
        /*
         * Here the thing, at this point we have 2 exceptions:
         * $errorRendererError  - the error caused by error renderer
         * $error               - the main error
         *
         * We need to show both exceptions, without loss of trace information, so use a bit of magic to merge them.
         *
         * Use exception nesting feature: rethrow the exceptions, an exception thrown in a finally block
         * will take unhandled exception as previous.
         * So PHP will add $error Exception as previous to $errorRendererError Exception to keep full error stack.
         */
        try {
            try {
                throw $error;
            } finally {
                throw $errorRendererError;
            }
        } catch (\Throwable $finalError) {
            throw $finalError;
        }
    } else {
        throw $error;
    }
}