PageRenderTime 26ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 1ms

/library/core/functions.error.php

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