[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Exception/ -> ExceptionHandler.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright  (C) 2012 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   8   */
   9  
  10  namespace Joomla\CMS\Exception;
  11  
  12  use Joomla\CMS\Application\CMSApplication;
  13  use Joomla\CMS\Error\AbstractRenderer;
  14  use Joomla\CMS\Factory;
  15  use Joomla\CMS\Log\Log;
  16  
  17  // phpcs:disable PSR1.Files.SideEffects
  18  \defined('JPATH_PLATFORM') or die;
  19  // phpcs:enable PSR1.Files.SideEffects
  20  
  21  /**
  22   * Displays the custom error page when an uncaught exception occurs.
  23   *
  24   * @since  3.0
  25   */
  26  class ExceptionHandler
  27  {
  28      /**
  29       * Handles an error triggered with the E_USER_DEPRECATED level.
  30       *
  31       * @param   integer  $errorNumber   The level of the raised error, represented by the E_* constants.
  32       * @param   string   $errorMessage  The error message.
  33       * @param   string   $errorFile     The file the error was triggered from.
  34       * @param   integer  $errorLine     The line number the error was triggered from.
  35       *
  36       * @return  boolean
  37       *
  38       * @since   4.0.0
  39       */
  40      public static function handleUserDeprecatedErrors(int $errorNumber, string $errorMessage, string $errorFile, int $errorLine): bool
  41      {
  42          // We only want to handle user deprecation messages, these will be triggered in code
  43          if ($errorNumber === E_USER_DEPRECATED) {
  44              try {
  45                  Log::add("$errorMessage - $errorFile - Line $errorLine", Log::WARNING, 'deprecated');
  46              } catch (\Exception $e) {
  47                  // Silence
  48              }
  49  
  50              // If debug mode is enabled, we want to let PHP continue to handle the error; otherwise, we can bail early
  51              if (\defined('JDEBUG') && JDEBUG) {
  52                  return true;
  53              }
  54          }
  55  
  56          // Always return false, this will tell PHP to handle the error internally
  57          return false;
  58      }
  59  
  60      /**
  61       * Handles exceptions: logs errors and renders error page.
  62       *
  63       * @param   \Exception|\Throwable  $error  An Exception or Throwable (PHP 7+) object for which to render the error page.
  64       *
  65       * @return  void
  66       *
  67       * @since   3.10.0
  68       */
  69      public static function handleException(\Throwable $error)
  70      {
  71          static::logException($error);
  72          static::render($error);
  73      }
  74  
  75      /**
  76       * Render the error page based on an exception.
  77       *
  78       * @param   \Throwable  $error  An Exception or Throwable (PHP 7+) object for which to render the error page.
  79       *
  80       * @return  void
  81       *
  82       * @since   3.0
  83       */
  84      public static function render(\Throwable $error)
  85      {
  86          try {
  87              $app = Factory::getApplication();
  88  
  89              // Flag if we are on cli
  90              $isCli = $app->isClient('cli');
  91  
  92              // If site is offline and it's a 404 error, just go to index (to see offline message, instead of 404)
  93              if (!$isCli && $error->getCode() == '404' && $app->get('offline') == 1) {
  94                  $app->redirect('index.php');
  95              }
  96  
  97              /*
  98               * Try and determine the format to render the error page in
  99               *
 100               * First we check if a Document instance was registered to Factory and use the type from that if available
 101               * If a type doesn't exist for that format, we try to use the format from the application's Input object
 102               * Lastly, if all else fails, we default onto the HTML format to at least render something
 103               */
 104              if (Factory::$document) {
 105                  $format = Factory::$document->getType();
 106              } else {
 107                  $format = $app->input->getString('format', 'html');
 108              }
 109  
 110              try {
 111                  $renderer = AbstractRenderer::getRenderer($format);
 112              } catch (\InvalidArgumentException $e) {
 113                  // Default to the HTML renderer
 114                  $renderer = AbstractRenderer::getRenderer('html');
 115              }
 116  
 117              // Reset the document object in the factory, this gives us a clean slate and lets everything render properly
 118              Factory::$document = $renderer->getDocument();
 119              Factory::getApplication()->loadDocument(Factory::$document);
 120  
 121              $data = $renderer->render($error);
 122  
 123              // If nothing was rendered, just use the message from the Exception
 124              if (empty($data)) {
 125                  $data = $error->getMessage();
 126              }
 127  
 128              if ($isCli) {
 129                  echo $data;
 130              } else {
 131                  /** @var CMSApplication $app */
 132  
 133                  // Do not allow cache
 134                  $app->allowCache(false);
 135  
 136                  $app->setBody($data);
 137              }
 138  
 139              // This return is needed to ensure the test suite does not trigger the non-Exception handling below
 140              return;
 141          } catch (\Throwable $errorRendererError) {
 142              // Pass the error down
 143          }
 144  
 145          /*
 146           * To reach this point in the code means there was an error creating the error page.
 147           *
 148           * Let global handler to handle the error, @see bootstrap.php
 149           */
 150          if (isset($errorRendererError)) {
 151              /*
 152               * Here the thing, at this point we have 2 exceptions:
 153               * $errorRendererError  - the error caused by error renderer
 154               * $error               - the main error
 155               *
 156               * We need to show both exceptions, without loss of trace information, so use a bit of magic to merge them.
 157               *
 158               * Use exception nesting feature: rethrow the exceptions, an exception thrown in a finally block
 159               * will take unhandled exception as previous.
 160               * So PHP will add $error Exception as previous to $errorRendererError Exception to keep full error stack.
 161               */
 162              try {
 163                  try {
 164                      throw $error;
 165                  } finally {
 166                      throw $errorRendererError;
 167                  }
 168              } catch (\Throwable $finalError) {
 169                  throw $finalError;
 170              }
 171          } else {
 172              throw $error;
 173          }
 174      }
 175  
 176      /**
 177       * Checks if given error belong to PHP exception class (\Throwable for PHP 7+, \Exception for PHP 5-).
 178       *
 179       * @param   mixed  $error  Any error value.
 180       *
 181       * @return  boolean
 182       *
 183       * @since   3.10.0
 184       */
 185      protected static function isException($error)
 186      {
 187          return $error instanceof \Throwable;
 188      }
 189  
 190      /**
 191       * Logs exception, catching all possible errors during logging.
 192       *
 193       * @param   \Throwable  $error  An Exception or Throwable (PHP 7+) object to get error message from.
 194       *
 195       * @return  void
 196       *
 197       * @since   3.10.0
 198       */
 199      protected static function logException(\Throwable $error)
 200      {
 201          // Try to log the error, but don't let the logging cause a fatal error
 202          try {
 203              Log::add(
 204                  sprintf(
 205                      'Uncaught Throwable of type %1$s thrown with message "%2$s". Stack trace: %3$s',
 206                      \get_class($error),
 207                      $error->getMessage(),
 208                      $error->getTraceAsString()
 209                  ),
 210                  Log::CRITICAL,
 211                  'error'
 212              );
 213          } catch (\Throwable $e) {
 214              // Logging failed, don't make a stink about it though
 215          }
 216      }
 217  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer