1 <?php
  2 /**
  3  * @package     Joomla.Libraries
  4  * @subpackage  Error
  5  *
  6  * @copyright   Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
  7  * @license     GNU General Public License version 2 or later; see LICENSE.txt
  8  */
  9 
 10 defined('JPATH_PLATFORM') or die;
 11 
 12 /**
 13  * Displays the custom error page when an uncaught exception occurs.
 14  *
 15  * @since  3.0
 16  */
 17 class JErrorPage
 18 {
 19     /**
 20      * Render the error page based on an exception.
 21      *
 22      * @param   Exception|Throwable  $error  An Exception or Throwable (PHP 7+) object for which to render the error page.
 23      *
 24      * @return  void
 25      *
 26      * @since   3.0
 27      */
 28     public static function render($error)
 29     {
 30         $expectedClass = PHP_MAJOR_VERSION >= 7 ? 'Throwable' : 'Exception';
 31         $isException   = $error instanceof $expectedClass;
 32 
 33         // In PHP 5, the $error object should be an instance of Exception; PHP 7 should be a Throwable implementation
 34         if ($isException)
 35         {
 36             try
 37             {
 38                 // Try to log the error, but don't let the logging cause a fatal error
 39                 try
 40                 {
 41                     JLog::add(
 42                         sprintf(
 43                             'Uncaught %1$s of type %2$s thrown. Stack trace: %3$s',
 44                             $expectedClass,
 45                             get_class($error),
 46                             $error->getTraceAsString()
 47                         ),
 48                         JLog::CRITICAL,
 49                         'error'
 50                     );
 51                 }
 52                 catch (Throwable $e)
 53                 {
 54                     // Logging failed, don't make a stink about it though
 55                 }
 56                 catch (Exception $e)
 57                 {
 58                     // Logging failed, don't make a stink about it though
 59                 }
 60 
 61                 $app = JFactory::getApplication();
 62 
 63                 // If site is offline and it's a 404 error, just go to index (to see offline message, instead of 404)
 64                 if ($error->getCode() == '404' && $app->get('offline') == 1)
 65                 {
 66                     $app->redirect('index.php');
 67                 }
 68 
 69                 $attributes = array(
 70                     'charset'   => 'utf-8',
 71                     'lineend'   => 'unix',
 72                     'tab'       => "\t",
 73                     'language'  => 'en-GB',
 74                     'direction' => 'ltr',
 75                 );
 76 
 77                 // If there is a JLanguage instance in JFactory then let's pull the language and direction from its metadata
 78                 if (JFactory::$language)
 79                 {
 80                     $attributes['language']  = JFactory::getLanguage()->getTag();
 81                     $attributes['direction'] = JFactory::getLanguage()->isRtl() ? 'rtl' : 'ltr';
 82                 }
 83 
 84                 $document = JDocument::getInstance('error', $attributes);
 85 
 86                 if (!$document)
 87                 {
 88                     // We're probably in an CLI environment
 89                     jexit($error->getMessage());
 90                 }
 91 
 92                 // Get the current template from the application
 93                 $template = $app->getTemplate();
 94 
 95                 // Push the error object into the document
 96                 $document->setError($error);
 97 
 98                 if (ob_get_contents())
 99                 {
100                     ob_end_clean();
101                 }
102 
103                 $document->setTitle(JText::_('ERROR') . ': ' . $error->getCode());
104 
105                 $data = $document->render(
106                     false,
107                     array(
108                         'template'  => $template,
109                         'directory' => JPATH_THEMES,
110                         'debug'     => JDEBUG,
111                     )
112                 );
113 
114                 // Do not allow cache
115                 $app->allowCache(false);
116 
117                 // If nothing was rendered, just use the message from the Exception
118                 if (empty($data))
119                 {
120                     $data = $error->getMessage();
121                 }
122 
123                 $app->setBody($data);
124 
125                 echo $app->toString();
126 
127                 $app->close(0);
128 
129                 // This return is needed to ensure the test suite does not trigger the non-Exception handling below
130                 return;
131             }
132             catch (Throwable $e)
133             {
134                 // Pass the error down
135             }
136             catch (Exception $e)
137             {
138                 // Pass the error down
139             }
140         }
141 
142         // This isn't an Exception, we can't handle it.
143         if (!headers_sent())
144         {
145             header('HTTP/1.1 500 Internal Server Error');
146         }
147 
148         $message = 'Error displaying the error page';
149 
150         if ($isException)
151         {
152             // Make sure we do not display sensitive data in production environments
153             if (ini_get('display_errors'))
154             {
155                 $message .= ': ';
156 
157                 if (isset($e))
158                 {
159                     $message .= $e->getMessage() . ': ';
160                 }
161 
162                 $message .= $error->getMessage();
163             }
164         }
165 
166         echo $message;
167 
168         jexit(1);
169     }
170 }
171