2007-07-19 12:40:28 +02:00
< ? php
2008-02-25 03:10:37 +01:00
/**
2008-03-16 23:13:31 +01:00
* Supports debugging and core error handling .
*
* Attaches custom methods to the default
* error handling hooks in PHP . Currently , three levels
* of error are supported :
*
* - Notice
* - Warning
* - Error
*
* Notice level errors are currently unsupported , and will be passed
* directly to the normal PHP error output .
*
* Uncaught exceptions are currently passed to the debug
* reporter as standard PHP errors .
*
* There are four different types of error handler supported by the
* Debug class :
*
* - Friendly
* - Fatal
* - Logger
* - Emailer
*
* Currently , only Friendly , Fatal , and Emailer handlers are implemented .
*
* @ todo port header / footer wrapping code to external reporter class
* @ todo add support for user defined config : Debug :: die_on_notice ( true | false )
* @ todo add appropriate handling for E_NOTICE and E_USER_NOTICE levels
* @ todo better way of figuring out the error context to display in highlighted source
* @ todo implement error logger handler
2008-02-25 03:10:37 +01:00
*
* @ package sapphire
* @ subpackage core
2007-07-19 12:40:28 +02:00
*/
class Debug {
/**
2008-02-25 03:10:37 +01:00
* @ var $custom_smtp_server string Custom mailserver for sending mails .
2007-07-19 12:40:28 +02:00
*/
protected static $custom_smtp_server = '' ;
2008-02-25 03:10:37 +01:00
/**
* @ var $send_errors_to string Email address to send error notifications
*/
2007-07-19 12:40:28 +02:00
protected static $send_errors_to ;
2008-02-25 03:10:37 +01:00
/**
* @ var $send_warnings_to string Email address to send warning notifications
*/
2007-07-19 12:40:28 +02:00
protected static $send_warnings_to ;
2008-08-11 06:02:32 +02:00
/**
* String indicating the file where errors are logged .
* Filename is relative to the site root .
* The named file will have a terse log sent to it , and the full log ( an
* encoded file containing backtraces and things ) will go to a file of a similar
* name , but with the suffix " .full " added .
*/
protected static $log_errors_to = null ;
2007-07-19 12:40:28 +02:00
/**
* Show the contents of val in a debug - friendly way .
* Debug :: show () is intended to be equivalent to dprintr ()
*/
static function show ( $val , $showHeader = true ) {
if ( ! Director :: isLive ()) {
if ( $showHeader ) {
$caller = Debug :: caller ();
2008-08-13 03:47:05 +02:00
if ( Director :: is_ajax () || Director :: is_cli ())
2007-07-19 12:40:28 +02:00
echo " Debug ( $caller[class] $caller[type] $caller[function] () in line $caller[line] of " . basename ( $caller [ 'file' ]) . " ) \n " ;
else
2008-03-16 23:13:31 +01:00
echo " <div style= \" background-color: white; text-align: left; \" > \n <hr> \n <h3>Debug <span style= \" font-size: 65% \" >( $caller[class] $caller[type] $caller[function] () \n <span style= \" font-weight:normal \" >in line</span> $caller[line] \n <span style= \" font-weight:normal \" >of</span> " . basename ( $caller [ 'file' ]) . " )</span> \n </h3> \n " ;
2007-07-19 12:40:28 +02:00
}
echo Debug :: text ( $val );
2008-08-13 03:47:05 +02:00
if ( ! Director :: is_ajax () && ! Director :: is_cli ()) echo " </div> " ;
else echo " \n \n " ;
2007-07-19 12:40:28 +02:00
}
}
2008-02-25 03:10:37 +01:00
2008-08-09 08:24:30 +02:00
/**
* Close out the show dumper
*
* @ param mixed $val
*/
2007-07-19 12:40:28 +02:00
static function endshow ( $val ) {
if ( ! Director :: isLive ()) {
$caller = Debug :: caller ();
echo " <hr> \n <h3>Debug \n <span style= \" font-size: 65% \" >( $caller[class] $caller[type] $caller[function] () \n <span style= \" font-weight:normal \" >in line</span> $caller[line] \n <span style= \" font-weight:normal \" >of</span> " . basename ( $caller [ 'file' ]) . " )</span> \n </h3> \n " ;
echo Debug :: text ( $val );
die ();
}
}
2008-03-16 23:13:31 +01:00
2008-08-09 08:24:30 +02:00
/**
* Quick dump of a variable .
*
* @ param mixed $val
*/
2008-03-16 23:13:31 +01:00
static function dump ( $val ) {
2008-08-09 08:40:50 +02:00
echo '<pre style="background-color:#ccc;padding:5px;font-size:14px;line-height:18px;">' ;
2008-08-09 08:24:30 +02:00
$caller = Debug :: caller ();
2008-08-09 08:40:50 +02:00
echo " <span style= \" font-size: 12px;color:#666; \" >Line $caller[line] of " . basename ( $caller [ 'file' ]) . " :</span> \n " ;
if ( is_string ( $val )) print_r ( wordwrap ( $val , 100 ));
else print_r ( $val );
2008-03-16 23:13:31 +01:00
echo '</pre>' ;
}
2007-07-19 12:40:28 +02:00
2008-08-09 08:24:30 +02:00
/**
* ? ?
*
* @ param unknown_type $val
* @ return unknown
*/
2007-07-19 12:40:28 +02:00
static function text ( $val ) {
2008-02-25 03:10:37 +01:00
if ( is_object ( $val )) {
if ( method_exists ( $val , 'hasMethod' )) {
$hasDebugMethod = $val -> hasMethod ( 'debug' );
2008-02-25 02:06:39 +01:00
} else {
2008-02-25 03:10:37 +01:00
$hasDebugMethod = method_exists ( $val , 'debug' );
}
if ( $hasDebugMethod ) {
return $val -> debug ();
}
}
if ( is_array ( $val )) {
$result = " <ul> \n " ;
foreach ( $val as $k => $v ) {
$result .= " <li> $k = " . Debug :: text ( $v ) . " </li> \n " ;
2007-07-19 12:40:28 +02:00
}
2008-02-25 03:10:37 +01:00
$val = $result . " </ul> \n " ;
2007-07-19 12:40:28 +02:00
2008-02-25 03:10:37 +01:00
} else if ( is_object ( $val )) {
$val = var_export ( $val , true );
} else {
2008-08-13 03:47:05 +02:00
if ( ! Director :: is_cli () && ! Director :: is_ajax ()) {
2008-02-25 03:10:37 +01:00
$val = " <pre style= \" font-family: Courier new \" > " . htmlentities ( $val ) . " </pre> \n " ;
}
2007-07-19 12:40:28 +02:00
}
2008-02-25 03:10:37 +01:00
return $val ;
2007-07-19 12:40:28 +02:00
}
2007-09-14 05:19:34 +02:00
2007-07-19 12:40:28 +02:00
/**
* Show a debugging message
*/
static function message ( $message , $showHeader = true ) {
if ( ! Director :: isLive ()) {
$caller = Debug :: caller ();
$file = basename ( $caller [ 'file' ]);
2008-08-09 08:18:32 +02:00
if ( Director :: is_cli ()) {
if ( $showHeader ) echo " Debug (line $caller[line] of $file ): \n " ;
echo trim ( $message ) . " \n " ;
} else {
echo " <p style= \" background-color: white; color: black; width: 95%; margin: 0.5em; padding: 0.3em; border: 1px #CCC solid \" > \n " ;
if ( $showHeader ) echo " <b>Debug (line $caller[line] of $file ):</b> \n " ;
echo Convert :: raw2xml ( trim ( $message )) . " </p> \n " ;
}
2007-07-19 12:40:28 +02:00
}
}
2009-07-27 05:52:40 +02:00
// Keep track of how many headers have been sent
static $headerCount = 0 ;
/**
* Send a debug message in an HTTP header . Only works if you are
* on Dev , and headers have not yet been sent .
*
* @ param string $msg
* @ param string $prefix ( optional )
* @ return void
*/
static function header ( $msg , $prefix ) {
if ( Director :: isDev () && ! headers_sent ()) {
self :: $headerCount ++ ;
header ( 'SS-' . self :: $headerCount . ( $prefix ? '-' . $prefix : '' ) . ': ' . $msg );
}
}
2008-08-09 08:24:30 +02:00
2008-08-11 00:52:52 +02:00
/**
* Log to a standard text file output .
*
* @ param $message string to output
*/
static function log ( $message ) {
$file = dirname ( __FILE__ ) . '/../../debug.log' ;
$now = date ( 'r' );
2009-07-17 01:48:41 +02:00
$oldcontent = ( file_exists ( $file )) ? file_get_contents ( $file ) : '' ;
2008-08-11 00:52:52 +02:00
$content = $oldcontent . " \n \n == $now == \n $message\n " ;
file_put_contents ( $file , $content );
}
2007-07-19 12:40:28 +02:00
/**
2008-08-13 23:49:52 +02:00
* Load error handlers into environment .
* Caution : The error levels default to E_ALL is the site is in dev - mode ( set in main . php ) .
2007-07-19 12:40:28 +02:00
*/
static function loadErrorHandlers () {
2008-08-13 01:50:28 +02:00
set_error_handler ( 'errorHandler' , error_reporting ());
2008-02-25 03:10:37 +01:00
set_exception_handler ( 'exceptionHandler' );
2007-07-19 12:40:28 +02:00
}
2008-08-09 08:24:30 +02:00
/**
* Handle a non - fatal warning error thrown by PHP interpreter .
*
* @ param unknown_type $errno
* @ param unknown_type $errstr
* @ param unknown_type $errfile
* @ param unknown_type $errline
* @ param unknown_type $errcontext
*/
2007-07-19 12:40:28 +02:00
static function warningHandler ( $errno , $errstr , $errfile , $errline , $errcontext ) {
2008-02-25 03:10:37 +01:00
if ( error_reporting () == 0 ) return ;
2007-07-19 12:40:28 +02:00
if ( self :: $send_warnings_to ) self :: emailError ( self :: $send_warnings_to , $errno , $errstr , $errfile , $errline , $errcontext , " Warning " );
2008-08-11 06:02:32 +02:00
self :: log_error_if_necessary ( $errno , $errstr , $errfile , $errline , $errcontext , " Warning " );
2007-09-14 05:19:34 +02:00
2007-07-19 12:40:28 +02:00
if ( Director :: isDev ()) {
2008-09-12 03:47:39 +02:00
self :: showError ( $errno , $errstr , $errfile , $errline , $errcontext , " Warning " );
2007-07-19 12:40:28 +02:00
}
}
2007-09-14 05:19:34 +02:00
2008-08-09 08:24:30 +02:00
/**
* Handle a fatal error , depending on the mode of the site ( ie : Dev , Test , or Live ) .
*
* Runtime execution dies immediately once the error is generated .
*
* @ param unknown_type $errno
* @ param unknown_type $errstr
* @ param unknown_type $errfile
* @ param unknown_type $errline
* @ param unknown_type $errcontext
*/
2007-07-19 12:40:28 +02:00
static function fatalHandler ( $errno , $errstr , $errfile , $errline , $errcontext ) {
2008-10-01 16:43:43 +02:00
2007-07-19 12:40:28 +02:00
if ( self :: $send_errors_to ) self :: emailError ( self :: $send_errors_to , $errno , $errstr , $errfile , $errline , $errcontext , " Error " );
2008-08-11 06:02:32 +02:00
self :: log_error_if_necessary ( $errno , $errstr , $errfile , $errline , $errcontext , " Error " );
if ( Director :: isDev () || Director :: is_cli ()) {
2008-09-12 03:47:39 +02:00
Debug :: showError ( $errno , $errstr , $errfile , $errline , $errcontext , " Error " );
2007-09-14 05:19:34 +02:00
2007-07-19 12:40:28 +02:00
} else {
2008-10-01 16:43:43 +02:00
Debug :: friendlyError ();
2007-07-19 12:40:28 +02:00
}
2008-08-11 06:02:32 +02:00
exit ( 1 );
2007-07-19 12:40:28 +02:00
}
2008-08-09 08:24:30 +02:00
/**
* Render a user - facing error page , using the default HTML error template
2008-10-01 16:43:43 +02:00
* rendered by { @ link ErrorPage } if it exists . Doesn ' t use the standard { @ link HTTPResponse } class
* the keep dependencies minimal .
*
* @ uses ErrorPage
2008-08-09 08:24:30 +02:00
*
2008-10-01 16:43:43 +02:00
* @ param int $statusCode HTTP Status Code ( Default : 500 )
* @ param string $friendlyErrorMessage User - focused error message . Should not contain code pointers or " tech-speak " .
* Used in the HTTP Header and ajax responses .
* @ param string $friendlyErrorDetail Detailed user - focused message . Is just used if no { @ link ErrorPage } is found
* for this specific status code .
* @ return string HTML error message for non - ajax requests , plaintext for ajax - request .
2008-08-09 08:24:30 +02:00
*/
2008-10-01 16:43:43 +02:00
static function friendlyError ( $statusCode = 500 , $friendlyErrorMessage = null , $friendlyErrorDetail = null ) {
if ( ! $friendlyErrorMessage ) $friendlyErrorMessage = 'There has been an error' ;
if ( ! $friendlyErrorDetail ) $friendlyErrorDetail = 'The website server has not been able to respond to your request.' ;
2007-07-19 12:40:28 +02:00
2008-10-01 16:43:43 +02:00
if ( ! headers_sent ()) header ( $_SERVER [ 'SERVER_PROTOCOL' ] . " $statusCode $friendlyErrorMessage " );
2007-09-14 05:19:34 +02:00
2008-10-01 16:43:43 +02:00
if ( Director :: is_ajax ()) {
echo $friendlyErrorMessage ;
2007-07-19 12:40:28 +02:00
} else {
2009-05-20 00:11:33 +02:00
$errorFilePath = ErrorPage :: get_filepath_for_errorcode ( $statusCode , Translatable :: get_current_locale ());
2009-04-29 03:20:24 +02:00
if ( file_exists ( $errorFilePath )) {
2009-05-04 04:17:56 +02:00
echo file_get_contents ( $errorFilePath );
2007-07-19 12:40:28 +02:00
} else {
2008-10-01 16:43:43 +02:00
$renderer = new DebugView ();
$renderer -> writeHeader ();
$renderer -> writeInfo ( " Website Error " , $friendlyErrorMessage , $friendlyErrorDetail );
if ( Email :: getAdminEmail ()) {
$mailto = Email :: obfuscate ( Email :: getAdminEmail ());
$renderer -> writeParagraph ( 'Contact an administrator: ' . $mailto . '' );
}
$renderer -> writeFooter ();
2007-07-19 12:40:28 +02:00
}
}
}
2008-08-11 06:02:32 +02:00
/**
* Create an instance of an appropriate DebugView object .
*/
static function create_debug_view () {
2008-09-16 20:12:07 +02:00
if ( Director :: is_cli () || Director :: is_ajax ()) return new CliDebugView ();
2008-08-11 06:02:32 +02:00
else return new DebugView ();
}
2007-07-19 12:40:28 +02:00
2008-08-09 08:24:30 +02:00
/**
* Render a developer facing error page , showing the stack trace and details
* of the code where the error occured .
*
* @ param unknown_type $errno
* @ param unknown_type $errstr
* @ param unknown_type $errfile
* @ param unknown_type $errline
* @ param unknown_type $errcontext
*/
2008-09-12 03:47:39 +02:00
static function showError ( $errno , $errstr , $errfile , $errline , $errcontext , $errtype ) {
if ( ! headers_sent ()) {
$errText = " $errtype : \" $errstr\ " at line $errline of $errfile " ;
$errText = str_replace ( array ( " \n " , " \r " ), " " , $errText );
2008-10-01 16:43:43 +02:00
if ( ! headers_sent ()) header ( $_SERVER [ 'SERVER_PROTOCOL' ] . " 500 $errText " );
2008-09-16 20:15:26 +02:00
// if error is displayed through ajax with CliDebugView, use plaintext output
if ( Director :: is_ajax ()) header ( 'Content-Type: text/plain' );
2008-09-12 03:47:39 +02:00
}
2008-09-16 20:12:07 +02:00
// Legacy error handling for customized prototype.js Ajax.Base.responseIsSuccess()
// if(Director::is_ajax()) echo "ERROR:\n";
$reporter = self :: create_debug_view ();
// Coupling alert: This relies on knowledge of how the director gets its URL, it could be improved.
$httpRequest = isset ( $_SERVER [ 'REQUEST_URI' ]) ? $_SERVER [ 'REQUEST_URI' ] : $_REQUEST [ 'url' ];
if ( isset ( $_SERVER [ 'REQUEST_METHOD' ])) $httpRequest = $_SERVER [ 'REQUEST_METHOD' ] . ' ' . $httpRequest ;
2008-08-11 06:02:32 +02:00
2008-09-16 20:12:07 +02:00
$reporter -> writeHeader ( $httpRequest );
$reporter -> writeError ( $httpRequest , $errno , $errstr , $errfile , $errline , $errcontext );
2008-08-11 06:02:32 +02:00
2008-09-16 20:12:07 +02:00
$lines = file ( $errfile );
2008-08-13 05:54:50 +02:00
2008-09-16 20:12:07 +02:00
// Make the array 1-based
array_unshift ( $lines , " " );
unset ( $lines [ 0 ]);
2008-08-13 05:54:50 +02:00
2008-09-16 20:12:07 +02:00
$offset = $errline - 10 ;
$lines = array_slice ( $lines , $offset , 16 , true );
$reporter -> writeSourceFragment ( $lines , $errline );
2008-08-11 06:02:32 +02:00
2009-01-20 22:30:12 +01:00
$reporter -> writeTrace (( $errcontext ? $errcontext : debug_backtrace ()));
2008-09-16 20:12:07 +02:00
$reporter -> writeFooter ();
exit ( 1 );
2007-07-19 12:40:28 +02:00
}
2008-08-09 08:18:32 +02:00
2008-08-09 08:24:30 +02:00
/**
* Utility method to render a snippet of PHP source code , from selected file
* and highlighting the given line number .
*
* @ param string $errfile
* @ param int $errline
*/
2008-08-09 08:18:32 +02:00
static function showLines ( $errfile , $errline ) {
$lines = file ( $errfile );
$offset = $errline - 10 ;
$lines = array_slice ( $lines , $offset , 16 );
echo '<pre>' ;
$offset ++ ;
foreach ( $lines as $line ) {
$line = htmlentities ( $line );
if ( $offset == $errline ) {
echo " <span> $offset </span> <span class= \" error \" > $line </span> " ;
} else {
echo " <span> $offset </span> $line " ;
}
$offset ++ ;
}
echo '</pre>' ;
}
2007-09-14 05:19:34 +02:00
2008-08-09 08:24:30 +02:00
/**
* Dispatch an email notification message when an error is triggered .
2008-09-22 18:03:19 +02:00
* Uses the native PHP mail () function .
2008-08-09 08:24:30 +02:00
*
2008-09-22 18:03:19 +02:00
* @ param string $emailAddress
* @ param string $errno
* @ param string $errstr
* @ param string $errfile
* @ param int $errline
* @ param string $errcontext
* @ param string $errorType " warning " or " error "
* @ return boolean
2008-08-09 08:24:30 +02:00
*/
2007-07-19 12:40:28 +02:00
static function emailError ( $emailAddress , $errno , $errstr , $errfile , $errline , $errcontext , $errorType = " Error " ) {
if ( strtolower ( $errorType ) == 'warning' ) {
$colour = " orange " ;
} else {
$colour = " red " ;
}
2007-09-14 05:19:34 +02:00
2007-07-19 12:40:28 +02:00
$data = " <div style= \" border: 5px $colour solid \" > \n " ;
$data .= " <p style= \" color: white; background-color: $colour ; margin: 0 \" > $errorType : $errstr <br /> At line $errline in $errfile\n <br /> \n <br /> \n </p> \n " ;
$data .= Debug :: backtrace ( true );
$data .= " </div> \n " ;
// override smtp-server if needed
2008-09-22 18:03:19 +02:00
if ( self :: $custom_smtp_server ) ini_set ( " SMTP " , self :: $custom_smtp_server );
2008-02-25 03:10:37 +01:00
$relfile = Director :: makeRelative ( $errfile );
if ( $relfile [ 0 ] == '/' ) $relfile = substr ( $relfile , 1 );
2008-09-22 18:03:19 +02:00
return mail ( $emailAddress , " $errorType at $relfile line $errline (http:// $_SERVER[HTTP_HOST] $_SERVER[REQUEST_URI] ) " , $data , " Content-type: text/html \n From: errors@silverstripe.com " );
2007-07-19 12:40:28 +02:00
}
2008-08-11 06:02:32 +02:00
/**
* Log the given error , if self :: $log_errors is set .
2008-09-22 18:03:19 +02:00
* Uses the native error_log () funtion in PHP .
*
* Format : [ d - M - Y h : i : s ] < type > at < file > line < line >: < errormessage > < url >
*
* @ todo Detect script path for CLI errors
* @ todo Log detailed errors to full file
2008-08-11 06:02:32 +02:00
*/
protected static function log_error_if_necessary ( $errno , $errstr , $errfile , $errline , $errcontext , $errtype ) {
if ( self :: $log_errors_to ) {
$shortFile = " ../ " . self :: $log_errors_to ;
$fullFile = $shortFile . '.full' ;
$relfile = Director :: makeRelative ( $errfile );
if ( $relfile [ 0 ] == '/' ) $relfile = substr ( $relfile , 1 );
$urlSuffix = " " ;
if ( isset ( $_SERVER [ 'HTTP_HOST' ]) && $_SERVER [ 'HTTP_HOST' ] && isset ( $_SERVER [ 'REQUEST_URI' ])) {
$urlSuffix = " (http:// $_SERVER[HTTP_HOST] $_SERVER[REQUEST_URI] ) " ;
}
error_log ( '[' . date ( 'd-M-Y h:i:s' ) . " ] $errtype at $relfile line $errline : $errstr $urlSuffix\n " , 3 , $shortFile );
}
}
2007-07-19 12:40:28 +02:00
/**
* @ param string $server IP - Address or domain
*/
static function set_custom_smtp_server ( $server ) {
self :: $custom_smtp_server = $server ;
}
/**
* @ return string
*/
static function get_custom_smtp_server () {
return self :: $custom_smtp_server ;
}
/**
* Send errors to the given email address .
* Can be used like so :
* if ( Director :: isLive ()) Debug :: send_errors_to ( " sam@silverstripe.com " );
2008-09-22 18:03:19 +02:00
*
* @ param string $emailAddress The email address to send errors to
2008-12-04 23:38:32 +01:00
* @ param string $sendWarnings Set to true to send warnings as well as errors ( Default : false )
2007-07-19 12:40:28 +02:00
*/
2008-12-04 23:38:32 +01:00
static function send_errors_to ( $emailAddress , $sendWarnings = false ) {
2007-07-19 12:40:28 +02:00
self :: $send_errors_to = $emailAddress ;
self :: $send_warnings_to = $sendWarnings ? $emailAddress : null ;
}
/**
* @ return string
*/
static function get_send_errors_to () {
return self :: $send_errors_to ;
}
2008-09-22 18:03:19 +02:00
/**
* @ param string $emailAddress
*/
static function send_warnings_to ( $emailAddress ) {
self :: $send_warnings_to = $emailAddress ;
}
2007-07-19 12:40:28 +02:00
/**
* @ return string
*/
static function get_send_warnings_to () {
return self :: $send_warnings_to ;
}
2008-08-11 06:02:32 +02:00
/**
* Call this to enable logging of errors .
*/
static function log_errors_to ( $logFile = " .sserrors " ) {
self :: $log_errors_to = $logFile ;
}
2007-07-19 12:40:28 +02:00
static function caller () {
$bt = debug_backtrace ();
$caller = $bt [ 2 ];
$caller [ 'line' ] = $bt [ 1 ][ 'line' ];
$caller [ 'file' ] = $bt [ 1 ][ 'file' ];
2007-08-27 07:13:43 +02:00
if ( ! isset ( $caller [ 'class' ])) $caller [ 'class' ] = '' ;
if ( ! isset ( $caller [ 'type' ])) $caller [ 'type' ] = '' ;
2007-07-19 12:40:28 +02:00
return $caller ;
}
2008-08-09 08:24:30 +02:00
/**
* Render or return a backtrace from the given scope .
*
* @ param unknown_type $returnVal
* @ param unknown_type $ignoreAjax
* @ return unknown
*/
2007-07-19 12:40:28 +02:00
static function backtrace ( $returnVal = false , $ignoreAjax = false ) {
$bt = debug_backtrace ();
2008-08-13 05:40:06 +02:00
$result = self :: get_rendered_backtrace ( $bt , Director :: is_cli () || ( Director :: is_ajax () && ! $ignoreAjax ));
if ( $returnVal ) {
return $result ;
} else {
echo $result ;
}
}
2008-08-11 06:02:32 +02:00
2008-08-13 05:40:06 +02:00
/**
* Render a backtrace array into an appropriate plain - text or HTML string .
* @ param $bt The trace array , as returned by debug_backtrace () or Exception :: getTrace () .
* @ param $plainText Set to false for HTML output , or true for plain - text output
*/
static function get_rendered_backtrace ( $bt , $plainText = false ) {
2007-07-19 12:40:28 +02:00
// Ingore functions that are plumbing of the error handler
2008-10-09 03:45:12 +02:00
$ignoredFunctions = array ( 'DebugView->writeTrace' , 'CliDebugView->writeTrace' ,
'Debug::emailError' , 'Debug::warningHandler' , 'Debug::fatalHandler' , 'errorHandler' , 'Debug::showError' ,
'Debug::backtrace' , 'exceptionHandler' );
2007-07-19 12:40:28 +02:00
while ( $bt && in_array ( self :: full_func_name ( $bt [ 0 ]), $ignoredFunctions ) ) {
array_shift ( $bt );
}
2008-02-25 03:10:37 +01:00
2008-03-16 23:13:31 +01:00
$result = " <ul> " ;
2007-07-19 12:40:28 +02:00
foreach ( $bt as $item ) {
2008-08-13 05:40:06 +02:00
if ( $plainText ) {
2007-07-19 12:40:28 +02:00
$result .= self :: full_func_name ( $item , true ) . " \n " ;
2008-08-11 06:02:32 +02:00
if ( isset ( $item [ 'line' ]) && isset ( $item [ 'file' ])) $result .= " line $item[line] of " . basename ( $item [ 'file' ]) . " \n " ;
$result .= " \n " ;
2007-07-19 12:40:28 +02:00
} else {
2008-03-16 23:13:31 +01:00
if ( $item [ 'function' ] == 'user_error' ) {
$name = $item [ 'args' ][ 0 ];
} else {
$name = self :: full_func_name ( $item , true );
}
2008-11-05 06:04:32 +01:00
$result .= " <li><b> " . htmlentities ( $name ) . " </b> \n <br /> \n " ;
2008-03-16 23:13:31 +01:00
$result .= isset ( $item [ 'line' ]) ? " Line $item[line] of " : '' ;
2008-11-05 06:04:32 +01:00
$result .= isset ( $item [ 'file' ]) ? htmlentities ( basename ( $item [ 'file' ])) : '' ;
2008-03-16 23:13:31 +01:00
$result .= " </li> \n " ;
2007-07-19 12:40:28 +02:00
}
}
2008-03-16 23:13:31 +01:00
$result .= " </ul> " ;
2008-08-13 05:40:06 +02:00
return $result ;
2007-07-19 12:40:28 +02:00
}
/**
* Return the full function name . If showArgs is set to true , a string representation of the arguments will be shown
*/
static function full_func_name ( $item , $showArgs = false ) {
$funcName = '' ;
if ( isset ( $item [ 'class' ])) $funcName .= $item [ 'class' ];
if ( isset ( $item [ 'type' ])) $funcName .= $item [ 'type' ];
if ( isset ( $item [ 'function' ])) $funcName .= $item [ 'function' ];
if ( $showArgs && isset ( $item [ 'args' ])) {
2008-02-25 03:10:37 +01:00
$args = array ();
foreach ( $item [ 'args' ] as $arg ) {
if ( ! is_object ( $arg ) || method_exists ( $arg , '__toString' )) {
$args [] = ( string ) $arg ;
} else {
$args [] = get_class ( $arg );
}
}
$funcName .= " ( " . implode ( " , " , $args ) . " ) " ;
2007-07-19 12:40:28 +02:00
}
return $funcName ;
}
2007-11-08 00:46:00 +01:00
/**
* Check if the user has permissions to run URL debug tools ,
* else redirect them to log in .
*/
static function require_developer_login () {
if ( Director :: isDev ()) {
return ;
}
if ( isset ( $_SESSION [ 'loggedInAs' ])) {
// We have to do some raw SQL here, because this method is called in Object::defineMethods().
// This means we have to be careful about what objects we create, as we don't want Object::defineMethods()
// being called again.
// This basically calls Permission::checkMember($_SESSION['loggedInAs'], 'ADMIN');
$memberID = $_SESSION [ 'loggedInAs' ];
2008-11-24 10:31:14 +01:00
$groups = DB :: query ( " SELECT \" GroupID \" from \" Group_Members \" WHERE \" MemberID \" = " . $memberID );
2007-11-08 00:46:00 +01:00
$groupCSV = implode ( $groups -> column (), ',' );
$permission = DB :: query ( "
2008-11-24 10:31:14 +01:00
SELECT \ " ID \"
FROM \ " Permission \"
2007-11-08 00:46:00 +01:00
WHERE (
2008-11-24 10:31:14 +01:00
\ " Code \" = 'ADMIN'
AND \ " Type \" = " . Permission :: GRANT_PERMISSION . "
AND \ " GroupID \" IN ( $groupCSV )
2007-11-08 00:46:00 +01:00
)
" )->value();
if ( $permission ) {
return ;
}
}
// This basically does the same as
// Security::permissionFailure(null, "You need to login with developer access to make use of debugging tools.");
// We have to do this because of how early this method is called in execution.
$_SESSION [ 'Security' ][ 'Message' ][ 'message' ] = " You need to login with developer access to make use of debugging tools. " ;
$_SESSION [ 'Security' ][ 'Message' ][ 'type' ] = 'warning' ;
$_SESSION [ 'BackURL' ] = $_SERVER [ 'REQUEST_URI' ];
2008-10-01 16:43:43 +02:00
header ( $_SERVER [ 'SERVER_PROTOCOL' ] . " 302 Found " );
2007-11-08 00:46:00 +01:00
header ( " Location: " . Director :: baseURL () . " Security/login " );
die ();
}
2007-07-19 12:40:28 +02:00
}
2008-09-22 18:03:19 +02:00
2008-08-09 08:24:30 +02:00
/**
* Generic callback , to catch uncaught exceptions when they bubble up to the top of the call chain .
*
* @ ignore
2008-09-22 18:03:19 +02:00
* @ param Exception $exception
2008-08-09 08:24:30 +02:00
*/
2008-02-25 03:10:37 +01:00
function exceptionHandler ( $exception ) {
$errno = E_USER_ERROR ;
$type = get_class ( $exception );
$message = " Uncaught " . $type . " : " . $exception -> getMessage ();
$file = $exception -> getFile ();
$line = $exception -> getLine ();
$context = $exception -> getTrace ();
Debug :: fatalHandler ( $errno , $message , $file , $line , $context );
}
2008-08-09 08:24:30 +02:00
/**
* Generic callback to catch standard PHP runtime errors thrown by the interpreter
* or manually triggered with the user_error function .
2008-08-13 23:49:52 +02:00
* Caution : The error levels default to E_ALL is the site is in dev - mode ( set in main . php ) .
2008-08-09 08:24:30 +02:00
*
* @ ignore
2008-09-22 18:03:19 +02:00
* @ param int $errno
* @ param string $errstr
* @ param string $errfile
* @ param int $errline
2008-08-09 08:24:30 +02:00
*/
2009-03-22 09:38:14 +01:00
function errorHandler ( $errno , $errstr , $errfile , $errline ) {
$bt = debug_backtrace ();
2007-07-19 12:40:28 +02:00
switch ( $errno ) {
case E_ERROR :
case E_CORE_ERROR :
case E_USER_ERROR :
2009-06-17 13:36:49 +02:00
Debug :: fatalHandler ( $errno , $errstr , $errfile , $errline , null );
2007-07-19 12:40:28 +02:00
break ;
2008-03-16 23:13:31 +01:00
case E_NOTICE :
2007-07-19 12:40:28 +02:00
case E_WARNING :
case E_CORE_WARNING :
case E_USER_WARNING :
2009-06-17 13:36:49 +02:00
Debug :: warningHandler ( $errno , $errstr , $errfile , $errline , null );
2007-07-19 12:40:28 +02:00
break ;
2008-03-16 23:13:31 +01:00
2007-07-19 12:40:28 +02:00
}
}