2016-09-01 23:13:23 +02:00
< ? php
/*****************************************************************************
* ErrorHandler . php
* Contains key class ErrorHandler .
*
2016-09-02 11:17:10 +02:00
* Kabuki CMS ( C ) 2013 - 2016 , Aaron van Geffen
2016-09-01 23:13:23 +02:00
*****************************************************************************/
class ErrorHandler
{
private static $error_count = 0 ;
private static $handling_error ;
2016-09-02 11:17:10 +02:00
public static function enable ()
{
set_error_handler ( 'ErrorHandler::handleError' );
ini_set ( " display_errors " , DEBUG ? " On " : " Off " );
}
public static function disable ()
{
set_error_handler ( NULL );
ini_set ( " display_errors " , " Off " );
}
2016-09-01 23:13:23 +02:00
// Handler for standard PHP error messages.
public static function handleError ( $error_level , $error_message , $file , $line , $context = null )
{
// Don't handle suppressed errors (e.g. through @ operator)
if ( ! ( error_reporting () & $error_level ))
return ;
// Prevent recursing if we've messed up in this code path.
if ( self :: $handling_error )
return ;
self :: $error_count ++ ;
self :: $handling_error = true ;
// Basically, htmlspecialchars it, minus '&' for HTML entities.
$error_message = strtr ( $error_message , [ '<' => '<' , '>' => '>' , '"' => '"' , " \t " => ' ' ]);
$error_message = strtr ( $error_message , [ '<br>' => " <br> " , '<br />' => " <br> " , '<b>' => '<strong>' , '</b>' => '</strong>' , '<pre>' => '<pre>' , '</pre>' => '</pre>' ]);
// Generate a bunch of useful information to ease debugging later.
$debug_info = self :: getDebugInfo ( debug_backtrace ());
// Log the error in the database.
self :: logError ( $error_message , $debug_info , $file , $line );
// Are we considering this fatal? Then display and exit.
// !!! TODO: should we consider warnings fatal?
if ( true ) // DEBUG || (!DEBUG && $error_level === E_WARNING || $error_level === E_USER_WARNING))
self :: display ( $file . ' (' . $line . ')<br>' . $error_message , $debug_info );
// If it wasn't a fatal error, well...
self :: $handling_error = false ;
}
public static function getDebugInfo ( array $trace )
{
$debug_info = " Backtrace: \n " ;
$debug_info .= self :: formatBacktrace ( $trace );
// Include info on the contents of superglobals.
if ( ! empty ( $_SESSION ))
2023-03-28 19:21:19 +02:00
$debug_info .= " \n SESSION: " . var_export ( $_SESSION , true );
2016-09-01 23:13:23 +02:00
if ( ! empty ( $_POST ))
2023-03-28 19:21:19 +02:00
$debug_info .= " \n POST: " . var_export ( $_POST , true );
2016-09-01 23:13:23 +02:00
if ( ! empty ( $_GET ))
2023-03-28 19:21:19 +02:00
$debug_info .= " \n GET: " . var_export ( $_GET , true );
2016-09-01 23:13:23 +02:00
return $debug_info ;
}
private static function formatBacktrace ( array $trace )
{
$buffer = '' ;
$skipping = true ;
foreach ( $trace as $i => $call )
{
if ( isset ( $call [ 'class' ]) && ( $call [ 'class' ] === 'ErrorHandler' || $call [ 'class' ] === 'Database' ) ||
isset ( $call [ 'function' ]) && $call [ 'function' ] === 'preg_replace_callback' )
{
if ( ! $skipping )
{
$buffer .= " [...] \n " ;
$skipping = true ;
}
continue ;
}
else
$skipping = false ;
$file = isset ( $call [ 'file' ]) ? str_replace ( BASEDIR , '' , $call [ 'file' ]) : 'Unknown' ;
$object = isset ( $call [ 'class' ]) ? $call [ 'class' ] . $call [ 'type' ] : '' ;
$args = [];
2023-03-15 09:49:55 +01:00
if ( isset ( $call [ 'args' ]))
2016-09-01 23:13:23 +02:00
{
2023-03-15 09:49:55 +01:00
foreach ( $call [ 'args' ] as $j => $arg )
{
2023-03-28 19:21:07 +02:00
// Only include the class name for objects
if ( is_object ( $arg ))
$args [ $j ] = get_class ( $arg ) . '{}' ;
// Export everything else -- including arrays
else
$args [ $j ] = var_export ( $arg , true );
2023-03-15 09:49:55 +01:00
}
2016-09-01 23:13:23 +02:00
}
$buffer .= '#' . str_pad ( $i , 3 , ' ' )
. $object . $call [ 'function' ] . '(' . implode ( ', ' , $args ) . ')'
. ' called at [' . $file . ':' . $call [ 'line' ] . " ] \n " ;
}
return $buffer ;
}
// Logs an error into the database.
private static function logError ( $error_message = '' , $debug_info = '' , $file = '' , $line = 0 )
{
if ( ! ErrorLog :: log ([
'message' => $error_message ,
'debug_info' => $debug_info ,
'file' => str_replace ( BASEDIR , '' , $file ),
'line' => $line ,
'id_user' => Registry :: has ( 'user' ) ? Registry :: get ( 'user' ) -> getUserId () : 0 ,
'ip_address' => isset ( $_SERVER [ 'REMOTE_ADDR' ]) ? $_SERVER [ 'REMOTE_ADDR' ] : '' ,
'request_uri' => isset ( $_SERVER [ 'REQUEST_URI' ]) ? $_SERVER [ 'REQUEST_URI' ] : '' ,
]))
{
header ( 'HTTP/1.1 503 Service Temporarily Unavailable' );
echo '<h2>An Error Occured</h2><p>Our software could not connect to the database. We apologise for any inconvenience and ask you to check back later.</p>' ;
exit ;
}
return $error_message ;
}
2016-09-02 11:17:10 +02:00
public static function display ( $message , $debug_info , $is_sensitive = true )
2016-09-01 23:13:23 +02:00
{
2016-09-02 11:17:10 +02:00
$is_admin = Registry :: has ( 'user' ) && Registry :: get ( 'user' ) -> isAdmin ();
2016-09-01 23:13:23 +02:00
// Just show the message if we're running in a console.
if ( empty ( $_SERVER [ 'HTTP_HOST' ]))
{
echo $message ;
exit ;
}
2016-09-02 11:17:10 +02:00
// JSON request?
elseif ( isset ( $_GET [ 'json' ]) || isset ( $_GET [ 'format' ]) && $_GET [ 'format' ] == 'json' )
{
if ( DEBUG || $is_admin )
echo json_encode ([ 'error' => $message . " \n \n " . $debug_info ]);
elseif ( ! $is_sensitive )
echo json_encode ([ 'error' => $message ]);
else
echo json_encode ([ 'error' => 'Our apologies, an error occured while we were processing your request. Please try again later, or contact us if the problem persists.' ]);
exit ;
}
2016-09-01 23:13:23 +02:00
// Initialise the main template to present a nice message to the user.
$page = new MainTemplate ( 'An error occured!' );
// Show the error.
$is_admin = Registry :: has ( 'user' ) && Registry :: get ( 'user' ) -> isAdmin ();
if ( DEBUG || $is_admin )
{
$page -> adopt ( new DummyBox ( 'An error occured!' , '<p>' . $message . '</p><pre>' . $debug_info . '</pre>' ));
// Let's provide the admin navigation despite it all!
if ( $is_admin )
{
$page -> appendStylesheet ( BASEURL . '/css/admin.css' );
}
}
2016-09-02 11:17:10 +02:00
elseif ( ! $is_sensitive )
$page -> adopt ( new DummyBox ( 'An error occured!' , '<p>' . $message . '</p>' ));
2016-09-01 23:13:23 +02:00
else
$page -> adopt ( new DummyBox ( 'An error occured!' , '<p>Our apologies, an error occured while we were processing your request. Please try again later, or contact us if the problem persists.</p>' ));
// If we got this far, make sure we're not showing stuff twice.
ob_end_clean ();
// Render the page.
$page -> html_main ();
exit ;
}
}