PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/library/core/functions.error.php

https://bitbucket.org/bean/garden
PHP | 303 lines | 212 code | 35 blank | 56 comment | 75 complexity | 9c8fb054bd8cb8c6c6db63e9cb519f57 MD5 | raw file
Possible License(s): GPL-2.0, MIT, LGPL-2.1
  1. <?php if (!defined('APPLICATION')) exit();
  2. /*
  3. Copyright 2008, 2009 Vanilla Forums Inc.
  4. This file is part of Garden.
  5. Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
  6. Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  7. You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
  8. Contact Vanilla Forums Inc. at support [at] vanillaforums [dot] com
  9. */
  10. /**
  11. * A custom error handler that displays much more, very useful information when
  12. * errors are encountered in Garden.
  13. *
  14. * @param int The level of the error raised.
  15. * @param string The error message.
  16. * @param string The filename that the error was raised in.
  17. * @param string The line number the error was raised at.
  18. * @param string An array of every variable that existed in the scope the error was triggered in.
  19. */
  20. function Gdn_ErrorHandler($ErrorNumber, $Message, $File, $Line, $Arguments) {
  21. // Ignore errors that have a @ before them (ie. @function();)
  22. if (error_reporting() == 0)
  23. return FALSE;
  24. // Clean the output buffer in case an error was encountered in-page.
  25. @ob_end_clean();
  26. header('Content-Type: text/html; charset=utf-8');
  27. $SenderMessage = $Message;
  28. $SenderObject = 'PHP';
  29. $SenderMethod = 'Gdn_ErrorHandler';
  30. $SenderCode = FALSE;
  31. $MessageInfo = explode('|', $Message);
  32. $MessageCount = count($MessageInfo);
  33. if ($MessageCount == 4)
  34. list($SenderMessage, $SenderObject, $SenderMethod, $SenderCode) = $MessageInfo;
  35. else if ($MessageCount == 3)
  36. list($SenderMessage, $SenderObject, $SenderMethod) = $MessageInfo;
  37. $SenderMessage = strip_tags($SenderMessage);
  38. $Master = FALSE; // The parsed master view
  39. $CssPath = FALSE; // The web-path to the css file
  40. $ErrorLines = FALSE; // The lines near the error's line #
  41. $DeliveryType = DELIVERY_TYPE_ALL;
  42. if (array_key_exists('DeliveryType', $_POST)) {
  43. $DeliveryType = $_POST['DeliveryType'];
  44. } else if (array_key_exists('DeliveryType', $_GET)) {
  45. $DeliveryType = $_GET['DeliveryType'];
  46. }
  47. // Make sure all of the required custom functions and variables are defined.
  48. $PanicError = FALSE; // Should we just dump a message and forget about the master view?
  49. if (!defined('DS')) $PanicError = TRUE;
  50. if (!defined('PATH_ROOT')) $PanicError = TRUE;
  51. if (!defined('APPLICATION')) define('APPLICATION', 'Garden');
  52. if (!defined('APPLICATION_VERSION')) define('APPLICATION_VERSION', 'Unknown');
  53. $WebRoot = class_exists('Url', FALSE) ? Gdn_Url::WebRoot() : '';
  54. // Try and rollback a database transaction.
  55. if(class_exists('Gdn', FALSE)) {
  56. $Database = Gdn::Database();
  57. if(is_object($Database))
  58. $Database->RollbackTransaction();
  59. }
  60. if ($PanicError === FALSE) {
  61. // See if we can get the file that caused the error
  62. if (is_string($File) && is_numeric($ErrorNumber))
  63. $ErrorLines = @file($File);
  64. // If this error was encountered during an ajax request, don't bother gettting the css or theme files
  65. if ($DeliveryType == DELIVERY_TYPE_ALL) {
  66. $CssPaths = array(); // Potential places where the css can be found in the filesystem.
  67. $MasterViewPaths = array();
  68. $MasterViewName = 'error.master.php';
  69. $MasterViewCss = 'error.css';
  70. if(class_exists('Gdn', FALSE)) {
  71. $CurrentTheme = ''; // The currently selected theme
  72. $CurrentTheme = Gdn::Config('Garden.Theme', '');
  73. $MasterViewName = Gdn::Config('Garden.Errors.MasterView', $MasterViewName);
  74. $MasterViewCss = substr($MasterViewName, 0, strpos($MasterViewName, '.'));
  75. if ($MasterViewCss == '')
  76. $MasterViewCss = 'error';
  77. $MasterViewCss .= '.css';
  78. if ($CurrentTheme != '') {
  79. // Look for CSS in the theme folder:
  80. $CssPaths[] = PATH_THEMES . DS . $CurrentTheme . DS . 'design' . DS . $MasterViewCss;
  81. // Look for Master View in the theme folder:
  82. $MasterViewPaths[] = PATH_THEMES . DS . $CurrentTheme . DS . 'views' . DS . $MasterViewName;
  83. }
  84. }
  85. // Look for CSS in the dashboard design folder.
  86. $CssPaths[] = PATH_APPLICATIONS . DS . 'dashboard' . DS . 'design' . DS . $MasterViewCss;
  87. // Look for Master View in the dashboard view folder.
  88. $MasterViewPaths[] = PATH_APPLICATIONS . DS . 'dashboard' . DS . 'views' . DS . $MasterViewName;
  89. $CssPath = FALSE;
  90. $Count = count($CssPaths);
  91. for ($i = 0; $i < $Count; ++$i) {
  92. if (file_exists($CssPaths[$i])) {
  93. $CssPath = $CssPaths[$i];
  94. break;
  95. }
  96. }
  97. if ($CssPath !== FALSE) {
  98. $CssPath = str_replace(
  99. array(PATH_ROOT, DS),
  100. array('', '/'),
  101. $CssPath
  102. );
  103. $CssPath = ($WebRoot == '' ? '' : '/'. $WebRoot) . $CssPath;
  104. }
  105. $MasterViewPath = FALSE;
  106. $Count = count($MasterViewPaths);
  107. for ($i = 0; $i < $Count; ++$i) {
  108. if (file_exists($MasterViewPaths[$i])) {
  109. $MasterViewPath = $MasterViewPaths[$i];
  110. break;
  111. }
  112. }
  113. if ($MasterViewPath !== FALSE) {
  114. include($MasterViewPath);
  115. $Master = TRUE;
  116. }
  117. }
  118. }
  119. if ($DeliveryType != DELIVERY_TYPE_ALL) {
  120. // This is an ajax request, so dump an error that is more eye-friendly in the debugger
  121. echo '<h1>FATAL ERROR IN: ',$SenderObject,'.',$SenderMethod,"();</h1>\n<div class=\"AjaxError\">\"".$SenderMessage."\"\n";
  122. if ($SenderCode != '')
  123. echo htmlentities($SenderCode, ENT_COMPAT, 'UTF-8')."\n";
  124. if (is_array($ErrorLines) && $Line > -1)
  125. echo "LOCATION: ",$File,"\n";
  126. $LineCount = count($ErrorLines);
  127. $Padding = strlen($Line+5);
  128. for ($i = 0; $i < $LineCount; ++$i) {
  129. if ($i > $Line-6 && $i < $Line+4) {
  130. if ($i == $Line - 1)
  131. echo '>>';
  132. echo '> '.str_pad($i+1, $Padding, " ", STR_PAD_LEFT),': ',str_replace(array("\n", "\r"), array('', ''), $ErrorLines[$i]),"\n";
  133. }
  134. }
  135. $Backtrace = debug_backtrace();
  136. if (is_array($Backtrace)) {
  137. echo "BACKTRACE:\n";
  138. $BacktraceCount = count($Backtrace);
  139. for ($i = 0; $i < $BacktraceCount; ++$i) {
  140. if (array_key_exists('file', $Backtrace[$i])) {
  141. $File = $Backtrace[$i]['file'].' '
  142. .$Backtrace[$i]['line'];
  143. }
  144. echo '['.$File.']' , ' '
  145. ,array_key_exists('class', $Backtrace[$i]) ? $Backtrace[$i]['class'] : 'PHP'
  146. ,array_key_exists('type', $Backtrace[$i]) ? $Backtrace[$i]['type'] : '::'
  147. ,$Backtrace[$i]['function'],'();'
  148. ,"\n";
  149. }
  150. }
  151. echo '</div>';
  152. } else {
  153. // If the master view wasn't found, assume a panic state and dump the error.
  154. if ($Master === FALSE) {
  155. echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  156. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-ca">
  157. <head>
  158. <title>Fatal Error</title>
  159. </head>
  160. <body>
  161. <h1>Fatal Error in ',$SenderObject,'.',$SenderMethod,'();</h1>
  162. <h2>',$SenderMessage,"</h2>\n";
  163. if ($SenderCode != '')
  164. echo '<code>',htmlentities($SenderCode, ENT_COMPAT, 'UTF-8'),"</code>\n";
  165. if (is_array($ErrorLines) && $Line > -1) {
  166. echo '<h3><strong>The error occurred on or near:</strong> ',$File,'</h3>
  167. <pre>';
  168. $LineCount = count($ErrorLines);
  169. $Padding = strlen($Line+4);
  170. for ($i = 0; $i < $LineCount; ++$i) {
  171. if ($i > $Line-6 && $i < $Line+4) {
  172. echo str_pad($i, $Padding, " ", STR_PAD_LEFT),': ',htmlentities($ErrorLines[$i], ENT_COMPAT, 'UTF-8');
  173. }
  174. }
  175. echo "</pre>\n";
  176. }
  177. echo '<h2>Need Help?</h2>
  178. <p>If you are a user of this website, you can report this message to a website administrator.</p>
  179. <p>If you are an administrator of this website, you can get help at the <a href="http://vanillaforums.org/discussions/" target="_blank">Vanilla Community Forums</a>.</p>
  180. <h2>Additional information for support personnel:</h2>
  181. <ul>
  182. <li><strong>Application:</strong> ',APPLICATION,'</li>
  183. <li><strong>Application Version:</strong> ',APPLICATION_VERSION,'</li>
  184. <li><strong>PHP Version:</strong> ',PHP_VERSION,'</li>
  185. <li><strong>Operating System:</strong> ',PHP_OS,"</li>\n";
  186. if (array_key_exists('HTTP_REFERER', $_SERVER))
  187. echo '<li><strong>Referer:</strong> ',$_SERVER['HTTP_REFERER'],"</li>\n";
  188. if (array_key_exists('HTTP_USER_AGENT', $_SERVER))
  189. echo '<li><strong>User Agent:</strong> ',$_SERVER['HTTP_USER_AGENT'],"</li>\n";
  190. if (array_key_exists('REQUEST_URI', $_SERVER))
  191. echo '<li><strong>Request Uri:</strong> ',$_SERVER['REQUEST_URI'],"</li>\n";
  192. echo '</ul>
  193. </body>
  194. </html>';
  195. }
  196. }
  197. // Attempt to log an error message no matter what.
  198. LogMessage($File, $Line, $SenderObject, $SenderMethod, $SenderMessage, $SenderCode);
  199. exit();
  200. }
  201. if (!function_exists('ErrorMessage')) {
  202. /**
  203. * Returns an error message formatted in a way that the custom ErrorHandler
  204. * function can understand (allows a little more information to be displayed
  205. * on errors).
  206. *
  207. * @param string The actual error message.
  208. * @param string The name of the object that encountered the error.
  209. * @param string The name of the method that encountered the error.
  210. * @param string Any additional information that could be useful to debuggers.
  211. */
  212. function ErrorMessage($Message, $SenderObject, $SenderMethod, $Code = '') {
  213. return $Message.'|'.$SenderObject.'|'.$SenderMethod.'|'.$Code;
  214. }
  215. }
  216. if (!function_exists('LogMessage')) {
  217. /**
  218. * Logs errors to a file. This function does not throw errors because it is
  219. * a last-ditch effort after errors have already
  220. * been rendered.
  221. *
  222. * @param string The file to save the error log in.
  223. * @param int The line number that encountered the error.
  224. * @param string The name of the object that encountered the error.
  225. * @param string The name of the method that encountered the error.
  226. * @param string The error message.
  227. * @param string Any additional information that could be useful to debuggers.
  228. */
  229. function LogMessage($File, $Line, $Object, $Method, $Message, $Code = '') {
  230. // Figure out where to save the log
  231. if(class_exists('Gdn', FALSE)) {
  232. $LogErrors = Gdn::Config('Garden.Errors.LogEnabled', FALSE);
  233. if ($LogErrors === TRUE) {
  234. $Log = "[Garden] $File, $Line, $Object.$Method()";
  235. if ($Message <> '') $Log .= ", $Message";
  236. if ($Code <> '') $Log .= ", $Code";
  237. $Log .= "\n";
  238. // Fail silently (there could be permission issues on badly set up servers).
  239. $ErrorLogFile = Gdn::Config('Garden.Errors.LogFile');
  240. if ($ErrorLogFile == '') {
  241. @error_log($Log);
  242. } else {
  243. @error_log($Log, 3, $ErrorLogFile);
  244. }
  245. }
  246. }
  247. }
  248. }
  249. if (!function_exists('CleanErrorArguments')) {
  250. function CleanErrorArguments(&$Var, $BlackList = array('configuration', 'config', 'database', 'password')) {
  251. if (is_array($Var)) {
  252. foreach ($Var as $Key => $Value) {
  253. if (in_array(strtolower($Key), $BlackList)) {
  254. $Var[$Key] = 'SECURITY';
  255. } else {
  256. if (is_object($Value)) {
  257. $Value = Gdn_Format::ObjectAsArray($Value);
  258. $Var[$Key] = $Value;
  259. }
  260. if (is_array($Value))
  261. CleanErrorArguments($Var[$Key], $BlackList);
  262. }
  263. }
  264. }
  265. }
  266. }
  267. // Set up Garden to handle php errors
  268. set_error_handler('Gdn_ErrorHandler', E_ALL);