PageRenderTime 39ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/library/core/functions.error.php

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