2007-07-19 12:40:28 +02:00
< ? php
2008-02-25 03:10:37 +01:00
2008-01-09 05:18:36 +01:00
/**
2008-02-25 03:10:37 +01:00
* @ package sapphire
* @ subpackage core
*/
/**
* Supports debugging and core error handling via static methods .
*
* @ 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 ;
/**
* 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 ();
if ( Director :: is_ajax ())
echo " Debug ( $caller[class] $caller[type] $caller[function] () in line $caller[line] of " . basename ( $caller [ 'file' ]) . " ) \n " ;
else
2007-08-15 13:54:11 +02:00
echo " <div style= \" background-color: white; text-align: left; width: 50em \" > \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 );
if ( ! Director :: is_ajax ()) echo " </div> " ;
}
}
2008-02-25 03:10:37 +01:00
/**
* Emails the contents of the output buffer
*/
2007-07-19 12:40:28 +02:00
static function mailBuffer ( $email , $subject ) {
mail ( $email , $subject , ob_get_contents () );
ob_end_clean ();
}
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 ();
}
}
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 {
if ( true || ! Director :: is_ajax ()) {
$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' ]);
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-09-14 05:19:34 +02:00
2007-07-19 12:40:28 +02:00
/**
* Load an error handler
2008-02-25 03:10:37 +01:00
*
* @ todo why does this delegate to loadFatalErrorHandler ?
2007-07-19 12:40:28 +02:00
*/
static function loadErrorHandlers () {
Debug :: loadFatalErrorHandler ();
}
2007-09-14 05:19:34 +02:00
2008-02-25 03:10:37 +01:00
/**
* @ todo can this be moved into loadErrorHandlers ?
*/
2007-07-19 12:40:28 +02:00
static function loadFatalErrorHandler () {
2008-02-25 03:10:37 +01:00
set_error_handler ( 'errorHandler' , ( E_ALL ^ E_NOTICE ) ^ E_USER_NOTICE );
set_exception_handler ( 'exceptionHandler' );
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 " );
2007-09-14 05:19:34 +02:00
2007-07-19 12:40:28 +02:00
if ( Director :: isDev ()) {
2008-02-25 03:10:37 +01:00
self :: showError ( $errno , $errstr , $errfile , $errline , $errcontext );
2007-07-19 12:40:28 +02:00
}
}
2007-09-14 05:19:34 +02:00
2007-07-19 12:40:28 +02:00
static function fatalHandler ( $errno , $errstr , $errfile , $errline , $errcontext ) {
if ( self :: $send_errors_to ) self :: emailError ( self :: $send_errors_to , $errno , $errstr , $errfile , $errline , $errcontext , " Error " );
if ( Director :: isDev ()) {
Debug :: showError ( $errno , $errstr , $errfile , $errline , $errcontext );
2007-09-14 05:19:34 +02:00
2007-07-19 12:40:28 +02:00
} else {
Debug :: friendlyError ( $errno , $errstr , $errfile , $errline , $errcontext );
}
die ();
}
static function friendlyError ( $errno , $errstr , $errfile , $errline , $errcontext ) {
header ( " HTTP/1.0 500 Internal server error " );
if ( Director :: is_ajax ()) {
2008-02-25 03:10:37 +01:00
echo " There has been an error " ;
2007-09-14 05:19:34 +02:00
2007-07-19 12:40:28 +02:00
} else {
if ( file_exists ( '../assets/error-500.html' )) {
include ( '../assets/error-500.html' );
} else {
2008-02-25 03:10:37 +01:00
echo " <h1>Error</h1><p>The website server has not been able to respond to your request.</p> \n " ;
2007-07-19 12:40:28 +02:00
}
}
}
static function showError ( $errno , $errstr , $errfile , $errline , $errcontext ) {
if ( ! headers_sent ()) header ( " HTTP/1.0 500 Internal server error " );
if ( Director :: is_ajax ()) {
echo " ERROR:Error $errno : $errstr\n At l $errline in $errfile\n " ;
Debug :: backtrace ();
2007-09-14 05:19:34 +02:00
2007-07-19 12:40:28 +02:00
} else {
echo " <div style= \" border: 5px red solid \" > \n " ;
echo " <p style= \" color: white; background-color: red; margin: 0 \" >FATAL ERROR: $errstr <br /> \n At line $errline in $errfile <br /> \n <br /> \n </p> \n " ;
Debug :: backtrace ();
2008-02-25 03:10:37 +01:00
//Debug::show(debug_backtrace());
2007-09-14 05:19:34 +02:00
2007-07-19 12:40:28 +02:00
echo " <h2>Context</h2> \n " ;
Debug :: show ( $errcontext );
echo " </div> \n " ;
}
}
2007-09-14 05:19:34 +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
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 );
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
}
/**
* @ 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 " );
* @ param emailAddress The email address to send them to
* @ param sendWarnings Set to true to send warnings as well as errors .
*/
static function send_errors_to ( $emailAddress , $sendWarnings = true ) {
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 ;
}
/**
* @ return string
*/
static function get_send_warnings_to () {
return self :: $send_warnings_to ;
}
/**
* Deprecated . Send live errors and warnings to the given address .
2008-02-25 03:10:37 +01:00
* @ deprecated Use send_errors_to () instead .
2007-07-19 12:40:28 +02:00
*/
static function sendLiveErrorsTo ( $emailAddress ) {
2008-02-25 03:10:37 +01:00
user_error ( 'Debug::sendLiveErrorsTo() is deprecated. Use Debug::send_errors_to() instead.' , E_USER_NOTICE );
2007-07-19 12:40:28 +02:00
if ( ! Director :: isDev ()) self :: send_errors_to ( $emailAddress , true );
}
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 ;
}
static function backtrace ( $returnVal = false , $ignoreAjax = false ) {
$bt = debug_backtrace ();
// Ingore functions that are plumbing of the error handler
2008-02-25 03:10:37 +01:00
$ignoredFunctions = array ( '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
2007-07-19 12:40:28 +02:00
$result = " " ;
foreach ( $bt as $item ) {
if ( Director :: is_ajax () && ! $ignoreAjax ) {
$result .= self :: full_func_name ( $item , true ) . " \n " ;
$result .= " line $item[line] of " . basename ( $item [ 'file' ]) . " \n \n " ;
} else {
$result .= " <p><b> " . self :: full_func_name ( $item , true ) . " </b> \n <br /> \n " ;
$result .= isset ( $item [ 'line' ]) ? " line $item[line] of " : '' ;
$result .= isset ( $item [ 'file' ]) ? basename ( $item [ 'file' ]) : '' ;
$result .= " </p> \n " ;
}
}
2008-02-25 03:10:37 +01:00
if ( $returnVal ) {
return $result ;
} else {
echo $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' ];
$groups = DB :: query ( " SELECT GroupID from Group_Members WHERE MemberID= " . $memberID );
$groupCSV = implode ( $groups -> column (), ',' );
$permission = DB :: query ( "
SELECT ID
FROM Permission
WHERE (
Code = 'ADMIN'
AND Type = " . Permission::GRANT_PERMISSION . "
AND GroupID IN ( $groupCSV )
)
" )->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' ];
header ( " HTTP/1.1 302 Found " );
header ( " Location: " . Director :: baseURL () . " Security/login " );
die ();
}
2007-07-19 12:40:28 +02:00
}
2007-11-08 00:46:00 +01: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 );
}
2007-07-19 12:40:28 +02:00
function errorHandler ( $errno , $errstr , $errfile , $errline , $errcontext ) {
switch ( $errno ) {
case E_ERROR :
case E_CORE_ERROR :
case E_USER_ERROR :
Debug :: fatalHandler ( $errno , $errstr , $errfile , $errline , $errcontext );
break ;
case E_WARNING :
case E_CORE_WARNING :
case E_USER_WARNING :
Debug :: warningHandler ( $errno , $errstr , $errfile , $errline , $errcontext );
break ;
}
}
2007-11-08 00:46:00 +01:00
?>