PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/core/error_api.php

https://github.com/fusenigk/mantisbt-1
PHP | 449 lines | 264 code | 61 blank | 124 comment | 58 complexity | 53b2ec199cfb5f9313370dbaf1805908 MD5 | raw file
  1. <?php
  2. # MantisBT - A PHP based bugtracking system
  3. # MantisBT is free software: you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation, either version 2 of the License, or
  6. # (at your option) any later version.
  7. #
  8. # MantisBT is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with MantisBT. If not, see <http://www.gnu.org/licenses/>.
  15. /**
  16. * Error API
  17. *
  18. * @package CoreAPI
  19. * @subpackage ErrorAPI
  20. * @copyright Copyright (C) 2000 - 2002 Kenzaburo Ito - kenito@300baud.org
  21. * @copyright Copyright (C) 2002 - 2011 MantisBT Team - mantisbt-dev@lists.sourceforge.net
  22. * @link http://www.mantisbt.org
  23. *
  24. * @uses compress_api.php
  25. * @uses config_api.php
  26. * @uses constant_api.php
  27. * @uses database_api.php
  28. * @uses html_api.php
  29. * @uses lang_api.php
  30. */
  31. require_api( 'compress_api.php' );
  32. require_api( 'config_api.php' );
  33. require_api( 'constant_inc.php' );
  34. require_api( 'database_api.php' );
  35. require_api( 'html_api.php' );
  36. require_api( 'lang_api.php' );
  37. $g_error_parameters = array();
  38. $g_error_handled = false;
  39. $g_error_proceed_url = null;
  40. $g_error_send_page_header = true;
  41. set_error_handler( 'error_handler' );
  42. /**
  43. * Default error handler
  44. *
  45. * This handler will not receive E_ERROR, E_PARSE, E_CORE_*, or E_COMPILE_*
  46. * errors.
  47. *
  48. * E_USER_* are triggered by us and will contain an error constant in $p_error
  49. * The others, being system errors, will come with a string in $p_error
  50. *
  51. * @access private
  52. * @param int p_type contains the level of the error raised, as an integer.
  53. * @param string p_error contains the error message, as a string.
  54. * @param string p_file contains the filename that the error was raised in, as a string.
  55. * @param int p_line contains the line number the error was raised at, as an integer.
  56. * @param array p_context to the active symbol table at the point the error occurred (optional)
  57. * @uses lang_api.php
  58. * @uses config_api.php
  59. * @uses compress_api.php
  60. * @uses database_api.php (optional)
  61. * @uses html_api.php (optional)
  62. */
  63. function error_handler( $p_type, $p_error, $p_file, $p_line, $p_context ) {
  64. global $g_error_parameters, $g_error_handled, $g_error_proceed_url;
  65. global $g_lang_overrides;
  66. global $g_error_send_page_header;
  67. # check if errors were disabled with @ somewhere in this call chain
  68. if( 0 == error_reporting() ) {
  69. return;
  70. }
  71. $t_lang_pushed = false;
  72. $t_db_connected = false;
  73. if( function_exists( 'db_is_connected' ) ) {
  74. if( db_is_connected() ) {
  75. $t_db_connected = true;
  76. }
  77. }
  78. $t_html_api = false;
  79. if( function_exists( 'html_end' ) ) {
  80. $t_html_api = true;
  81. }
  82. # flush any language overrides to return to user's natural default
  83. if( $t_db_connected ) {
  84. lang_push( lang_get_default() );
  85. $t_lang_pushed = true;
  86. }
  87. $t_short_file = basename( $p_file );
  88. $t_method_array = config_get_global( 'display_errors' );
  89. if( isset( $t_method_array[$p_type] ) ) {
  90. $t_method = $t_method_array[$p_type];
  91. } else {
  92. if( isset( $t_method_array[E_ALL] ) ) {
  93. $t_method = $t_method_array[E_ALL];
  94. } else {
  95. $t_method = 'none';
  96. }
  97. }
  98. # build an appropriate error string
  99. switch( $p_type ) {
  100. case E_WARNING:
  101. $t_error_type = 'SYSTEM WARNING';
  102. $t_error_description = $p_error;
  103. break;
  104. case E_NOTICE:
  105. $t_error_type = 'SYSTEM NOTICE';
  106. $t_error_description = $p_error;
  107. break;
  108. case E_USER_ERROR:
  109. $t_error_type = "APPLICATION ERROR #$p_error";
  110. $t_error_description = error_string( $p_error );
  111. break;
  112. case E_USER_WARNING:
  113. $t_error_type = "APPLICATION WARNING #$p_error";
  114. $t_error_description = error_string( $p_error );
  115. break;
  116. case E_USER_NOTICE:
  117. # used for debugging
  118. $t_error_type = 'DEBUG';
  119. $t_error_description = $p_error;
  120. break;
  121. default:
  122. # shouldn't happen, just display the error just in case
  123. $t_error_type = '';
  124. $t_error_description = $p_error;
  125. }
  126. $t_error_description = nl2br( $t_error_description );
  127. switch( $t_method ) {
  128. case 'halt':
  129. # disable any further event callbacks
  130. if ( function_exists( 'event_clear_callbacks' ) ) {
  131. event_clear_callbacks();
  132. }
  133. $t_oblen = ob_get_length();
  134. if( error_handled() && $t_oblen > 0 ) {
  135. $t_old_contents = ob_get_contents();
  136. }
  137. # We need to ensure compression is off - otherwise the compression headers are output.
  138. compress_disable();
  139. # then clean the buffer, leaving output buffering on.
  140. if( $t_oblen > 0 ) {
  141. ob_clean();
  142. }
  143. # don't send the page header information if it has already been sent
  144. if( $g_error_send_page_header ) {
  145. if( $t_html_api ) {
  146. html_page_top1();
  147. if( $p_error != ERROR_DB_QUERY_FAILED && $t_db_connected == true ) {
  148. html_page_top2();
  149. } else {
  150. html_page_top2a();
  151. }
  152. } else {
  153. echo '<html><head><title>', $t_error_type, '</title></head><body>';
  154. }
  155. }
  156. echo '<div id="error-msg">';
  157. echo '<div class="error-type">' . $t_error_type . '</div>';
  158. echo '<div class="error-description">', $t_error_description, '</div>';
  159. echo '<div class="error-info">';
  160. if( null === $g_error_proceed_url ) {
  161. echo lang_get( 'error_no_proceed' );
  162. } else {
  163. echo '<a href="', $g_error_proceed_url, '">', lang_get( 'proceed' ), '</a>';
  164. }
  165. echo '</div>';
  166. if( ON == config_get_global( 'show_detailed_errors' ) ) {
  167. echo '<div class="error-details">';
  168. error_print_details( $p_file, $p_line, $p_context );
  169. echo '</div>';
  170. echo '<div class="error-trace">';
  171. error_print_stack_trace();
  172. echo '</div>';
  173. }
  174. echo '</div>';
  175. if( isset( $t_old_contents ) ) {
  176. echo '<div class="warning">Previous non-fatal errors occurred. Page contents follow.</div>';
  177. echo '<div id="old-contents">';
  178. echo $t_old_contents;
  179. echo '</div>';
  180. }
  181. if( $t_html_api ) {
  182. if( $p_error != ERROR_DB_QUERY_FAILED && $t_db_connected == true ) {
  183. html_page_bottom();
  184. } else {
  185. html_body_end();
  186. html_end();
  187. }
  188. } else {
  189. echo '</body></html>', "\n";
  190. }
  191. exit();
  192. case 'inline':
  193. echo '<div class="error-inline">', $t_error_type, ': ', $t_error_description, '</div>';
  194. $g_error_handled = true;
  195. break;
  196. default:
  197. # do nothing - note we treat this as we've not handled an error, so any redirects go through.
  198. }
  199. if( $t_lang_pushed ) {
  200. lang_pop();
  201. }
  202. $g_error_parameters = array();
  203. $g_error_proceed_url = null;
  204. }
  205. /**
  206. * Print out the error details including context
  207. * @param string $p_file
  208. * @param int $p_line
  209. * @param string $p_context
  210. * @return null
  211. */
  212. function error_print_details( $p_file, $p_line, $p_context ) {
  213. ?>
  214. <table class="width75">
  215. <tr>
  216. <td>Full path: <?php echo htmlentities( $p_file, ENT_COMPAT, 'UTF-8' );?></td>
  217. </tr>
  218. <tr>
  219. <td>Line: <?php echo $p_line?></td>
  220. </tr>
  221. <tr>
  222. <td>
  223. <?php error_print_context( $p_context )?>
  224. </td>
  225. </tr>
  226. </table>
  227. <?php
  228. }
  229. /**
  230. * Print out the variable context given
  231. * @param string $p_context
  232. * @return null
  233. */
  234. function error_print_context( $p_context ) {
  235. if( !is_array( $p_context ) ) {
  236. return;
  237. }
  238. echo '<table class="width100"><tr><th>Variable</th><th>Value</th><th>Type</th></tr>';
  239. # print normal variables
  240. foreach( $p_context as $t_var => $t_val ) {
  241. if( !is_array( $t_val ) && !is_object( $t_val ) ) {
  242. $t_type = gettype( $t_val );
  243. $t_val = htmlentities( (string) $t_val, ENT_COMPAT, 'UTF-8' );
  244. # Mask Passwords
  245. if( strpos( $t_var, 'password' ) !== false ) {
  246. $t_val = '**********';
  247. }
  248. echo '<tr><td>', $t_var, '</td><td>', $t_val, '</td><td>', $t_type, '</td></tr>', "\n";
  249. }
  250. }
  251. # print arrays
  252. foreach( $p_context as $t_var => $t_val ) {
  253. if( is_array( $t_val ) && ( $t_var != 'GLOBALS' ) ) {
  254. echo '<tr><td colspan="3"><br /><strong>', $t_var, '</strong></td></tr>';
  255. echo '<tr><td colspan="3">';
  256. error_print_context( $t_val );
  257. echo '</td></tr>';
  258. }
  259. }
  260. echo '</table>';
  261. }
  262. /**
  263. * Print out a stack trace
  264. * @return null
  265. * @uses error_alternate_class
  266. */
  267. function error_print_stack_trace() {
  268. echo '<table class="width75">';
  269. echo '<tr><th>Filename</th><th>Line</th><th></th><th></th><th>Function</th><th>Args</th></tr>';
  270. $t_stack = debug_backtrace();
  271. array_shift( $t_stack );
  272. # remove the call to this function from the stack trace
  273. array_shift( $t_stack );
  274. # remove the call to the error handler from the stack trace
  275. foreach( $t_stack as $t_frame ) {
  276. echo '<tr ', error_alternate_class(), '>';
  277. echo '<td>', ( isset( $t_frame['file'] ) ? htmlentities( $t_frame['file'], ENT_COMPAT, 'UTF-8' ) : '-' ), '</td><td>', ( isset( $t_frame['line'] ) ? $t_frame['line'] : '-' ), '</td><td>', ( isset( $t_frame['class'] ) ? $t_frame['class'] : '-' ), '</td><td>', ( isset( $t_frame['type'] ) ? $t_frame['type'] : '-' ), '</td><td>', ( isset( $t_frame['function'] ) ? $t_frame['function'] : '-' ), '</td>';
  278. $t_args = array();
  279. if( isset( $t_frame['args'] ) && !empty( $t_frame['args'] ) ) {
  280. foreach( $t_frame['args'] as $t_value ) {
  281. $t_args[] = error_build_parameter_string( $t_value );
  282. }
  283. echo '<td>( ', htmlentities( implode( $t_args, ', ' ), ENT_COMPAT, 'UTF-8' ), ' )</td></tr>';
  284. } else {
  285. echo '<td>-</td></tr>';
  286. }
  287. }
  288. echo '</table>';
  289. }
  290. /**
  291. * Build a string describing the parameters to a function
  292. * @param string $p_param
  293. * @param bool $p_showtype default true
  294. * @param int $p_depth default 0
  295. * @return string
  296. */
  297. function error_build_parameter_string( $p_param, $p_showtype = true, $p_depth = 0 ) {
  298. if( $p_depth++ > 10 ) {
  299. return '<strong>***Nesting Level Too Deep***</strong>';
  300. }
  301. if( is_array( $p_param ) ) {
  302. $t_results = array();
  303. foreach( $p_param as $t_key => $t_value ) {
  304. $t_results[] = '[' . error_build_parameter_string( $t_key, false, $p_depth ) . ']' . ' => ' . error_build_parameter_string( $t_value, false, $p_depth );
  305. }
  306. return '<Array> { ' . implode( $t_results, ', ' ) . ' }';
  307. }
  308. else if( is_object( $p_param ) ) {
  309. $t_results = array();
  310. $t_class_name = get_class( $p_param );
  311. $t_inst_vars = get_object_vars( $p_param );
  312. foreach( $t_inst_vars as $t_name => $t_value ) {
  313. $t_results[] = "[$t_name]" . ' => ' . error_build_parameter_string( $t_value, false, $p_depth );
  314. }
  315. return '<Object><' . $t_class_name . '> ( ' . implode( $t_results, ', ' ) . ' )';
  316. } else {
  317. if( $p_showtype ) {
  318. return '<' . gettype( $p_param ) . '>' . var_export( $p_param, true );
  319. } else {
  320. return var_export( $p_param, true );
  321. }
  322. }
  323. }
  324. /**
  325. * Return an error string (in the current language) for the given error.
  326. * @param int $p_error
  327. * @return string
  328. * @access public
  329. */
  330. function error_string( $p_error ) {
  331. global $g_error_parameters;
  332. # We pad the parameter array to make sure that we don't get errors if
  333. # the caller didn't give enough parameters for the error string
  334. $t_padding = array_pad( array(), 10, '' );
  335. $t_error = lang_get( $p_error, null, false );
  336. if( $t_error == '' ) {
  337. return lang_get( 'missing_error_string' ) . $p_error;
  338. }
  339. # ripped from string_api
  340. $t_string = call_user_func_array( 'sprintf', array_merge( array( $t_error ), $g_error_parameters, $t_padding ) );
  341. return preg_replace( "/&amp;(#[0-9]+|[a-z]+);/i", "&$1;", @htmlspecialchars( $t_string, ENT_COMPAT, 'UTF-8' ) );
  342. }
  343. /**
  344. * Check if we have handled an error during this page
  345. * Return true if an error has been handled, false otherwise
  346. * @return bool
  347. */
  348. function error_handled() {
  349. global $g_error_handled;
  350. return( true == $g_error_handled );
  351. }
  352. /**
  353. * Set additional info parameters to be used when displaying the next error
  354. * This function takes a variable number of parameters
  355. *
  356. * When writing internationalized error strings, note that you can change the
  357. * order of parameters in the string. See the PHP manual page for the
  358. * sprintf() function for more details.
  359. * @access public
  360. * @return null
  361. */
  362. function error_parameters() {
  363. global $g_error_parameters;
  364. $g_error_parameters = func_get_args();
  365. }
  366. /**
  367. * Set a url to give to the user to proceed after viewing the error
  368. * @access public
  369. * @param string p_url url given to user after viewing the error
  370. * @return null
  371. */
  372. function error_proceed_url( $p_url ) {
  373. global $g_error_proceed_url;
  374. $g_error_proceed_url = $p_url;
  375. }
  376. /**
  377. * Simple version of helper_alternate_class for use by error api only.
  378. * @access private
  379. * @return string representing css class
  380. */
  381. function error_alternate_class() {
  382. static $t_errindex = 1;
  383. if( 1 == $t_errindex++ % 2 ) {
  384. return 'class="row-1"';
  385. } else {
  386. return 'class="row-2"';
  387. }
  388. }