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