(merged from branches/roa. use "svn log -c <changeset> -g <module-svn-path>" for detailed commit message)

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@60230 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2008-08-09 06:24:30 +00:00
parent e033d105f6
commit 0362276b67

View File

@ -1,4 +1,10 @@
<?php <?php
/**
* @package sapphire
* @subpackage core
*/
/** /**
* Supports debugging and core error handling. * Supports debugging and core error handling.
* *
@ -26,7 +32,6 @@
* *
* Currently, only Friendly, Fatal, and Emailer handlers are implemented. * Currently, only Friendly, Fatal, and Emailer handlers are implemented.
* *
* @todo make sure these doc comments are synced with tickets in trac
* @todo port header/footer wrapping code to external reporter class * @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 support for user defined config: Debug::die_on_notice(true | false)
* @todo add appropriate handling for E_NOTICE and E_USER_NOTICE levels * @todo add appropriate handling for E_NOTICE and E_USER_NOTICE levels
@ -82,6 +87,11 @@ class Debug {
ob_end_clean(); ob_end_clean();
} }
/**
* Close out the show dumper
*
* @param mixed $val
*/
static function endshow($val) { static function endshow($val) {
if(!Director::isLive()) { if(!Director::isLive()) {
$caller = Debug::caller(); $caller = Debug::caller();
@ -91,12 +101,25 @@ class Debug {
} }
} }
/**
* Quick dump of a variable.
*
* @param mixed $val
*/
static function dump($val) { static function dump($val) {
echo '<pre style="background-color:#ccc;padding:5px;">'; echo '<pre style="background-color:#ccc;padding:5px;">';
$caller = Debug::caller();
echo "<span style=\"font-size: 60%\">Line $caller[line] of " . basename($caller['file']) . "</span>\n";
print_r($val); print_r($val);
echo '</pre>'; echo '</pre>';
} }
/**
* ??
*
* @param unknown_type $val
* @return unknown
*/
static function text($val) { static function text($val) {
if(is_object($val)) { if(is_object($val)) {
if(method_exists($val, 'hasMethod')) { if(method_exists($val, 'hasMethod')) {
@ -145,18 +168,25 @@ class Debug {
} }
} }
} }
/** /**
* Load an error handler * Load error handlers into environment
*/ */
static function loadErrorHandlers() { static function loadErrorHandlers() {
Debug::loadFatalErrorHandler();
//set_error_handler('errorHandler', (E_ALL ^ E_NOTICE) ^ E_USER_NOTICE); //set_error_handler('errorHandler', (E_ALL ^ E_NOTICE) ^ E_USER_NOTICE);
set_error_handler('errorHandler', E_ALL); set_error_handler('errorHandler', E_ALL);
set_exception_handler('exceptionHandler'); set_exception_handler('exceptionHandler');
} }
/**
* 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
*/
static function warningHandler($errno, $errstr, $errfile, $errline, $errcontext) { static function warningHandler($errno, $errstr, $errfile, $errline, $errcontext) {
if(error_reporting() == 0) return; if(error_reporting() == 0) return;
if(self::$send_warnings_to) self::emailError(self::$send_warnings_to, $errno, $errstr, $errfile, $errline, $errcontext, "Warning"); if(self::$send_warnings_to) self::emailError(self::$send_warnings_to, $errno, $errstr, $errfile, $errline, $errcontext, "Warning");
@ -166,6 +196,17 @@ class Debug {
} }
} }
/**
* 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
*/
static function fatalHandler($errno, $errstr, $errfile, $errline, $errcontext) { 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(self::$send_errors_to) self::emailError(self::$send_errors_to, $errno, $errstr, $errfile, $errline, $errcontext, "Error");
@ -177,6 +218,17 @@ class Debug {
} }
die(); die();
} }
/**
* Render a user-facing error page, using the default HTML error template
* if it exists.
*
* @param unknown_type $errno
* @param unknown_type $errstr
* @param unknown_type $errfile
* @param unknown_type $errline
* @param unknown_type $errcontext
*/
static function friendlyError($errno, $errstr, $errfile, $errline, $errcontext) { static function friendlyError($errno, $errstr, $errfile, $errline, $errcontext) {
header("HTTP/1.0 500 Internal server error"); header("HTTP/1.0 500 Internal server error");
@ -192,13 +244,23 @@ class Debug {
} }
} }
/**
* 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
*/
static function showError($errno, $errstr, $errfile, $errline, $errcontext) { static function showError($errno, $errstr, $errfile, $errline, $errcontext) {
if(!headers_sent()) header("HTTP/1.0 500 Internal server error"); if(!headers_sent()) header("HTTP/1.0 500 Internal server error");
if(Director::is_ajax()) { if(Director::is_ajax()) {
echo "ERROR:Error $errno: $errstr\n At l$errline in $errfile\n"; echo "ERROR:Error $errno: $errstr\n At l$errline in $errfile\n";
Debug::backtrace(); Debug::backtrace();
} else { } else {
$reporter = new DebugView(); $reporter = new DebugReporter();
$reporter->writeHeader(); $reporter->writeHeader();
echo '<div class="info">'; echo '<div class="info">';
echo "<h1>" . strip_tags($errstr) . "</h1>"; echo "<h1>" . strip_tags($errstr) . "</h1>";
@ -207,7 +269,7 @@ class Debug {
echo '</div>'; echo '</div>';
echo '<div class="trace"><h3>Source</h3>'; echo '<div class="trace"><h3>Source</h3>';
Debug::showLines($errfile, $errline); Debug::showLines($errfile, $errline);
echo '</pre><h3>Trace</h3>'; echo '<h3>Trace</h3>';
Debug::backtrace(); Debug::backtrace();
echo '</div>'; echo '</div>';
$reporter->writeFooter(); $reporter->writeFooter();
@ -215,6 +277,13 @@ class Debug {
} }
} }
/**
* 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
*/
static function showLines($errfile, $errline) { static function showLines($errfile, $errline) {
$lines = file($errfile); $lines = file($errfile);
$offset = $errline-10; $offset = $errline-10;
@ -233,6 +302,17 @@ class Debug {
echo '</pre>'; echo '</pre>';
} }
/**
* Dispatch an email notification message when an error is triggered.
*
* @param unknown_type $emailAddress
* @param unknown_type $errno
* @param unknown_type $errstr
* @param unknown_type $errfile
* @param unknown_type $errline
* @param unknown_type $errcontext
* @param unknown_type $errorType
*/
static function emailError($emailAddress, $errno, $errstr, $errfile, $errline, $errcontext, $errorType = "Error") { static function emailError($emailAddress, $errno, $errstr, $errfile, $errline, $errcontext, $errorType = "Error") {
if(strtolower($errorType) == 'warning') { if(strtolower($errorType) == 'warning') {
$colour = "orange"; $colour = "orange";
@ -315,6 +395,13 @@ class Debug {
return $caller; return $caller;
} }
/**
* Render or return a backtrace from the given scope.
*
* @param unknown_type $returnVal
* @param unknown_type $ignoreAjax
* @return unknown
*/
static function backtrace($returnVal = false, $ignoreAjax = false) { static function backtrace($returnVal = false, $ignoreAjax = false) {
$bt = debug_backtrace(); $bt = debug_backtrace();
@ -421,7 +508,12 @@ class Debug {
die(); die();
} }
} }
/**
* Generic callback, to catch uncaught exceptions when they bubble up to the top of the call chain.
*
* @ignore
* @param unknown_type $exception
*/
function exceptionHandler($exception) { function exceptionHandler($exception) {
$errno = E_USER_ERROR; $errno = E_USER_ERROR;
$type = get_class($exception); $type = get_class($exception);
@ -432,6 +524,17 @@ function exceptionHandler($exception) {
Debug::fatalHandler($errno, $message, $file, $line, $context); Debug::fatalHandler($errno, $message, $file, $line, $context);
} }
/**
* Generic callback to catch standard PHP runtime errors thrown by the interpreter
* or manually triggered with the user_error function.
*
* @ignore
* @param unknown_type $errno
* @param unknown_type $errstr
* @param unknown_type $errfile
* @param unknown_type $errline
* @param unknown_type $errcontext
*/
function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) { function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) {
switch($errno) { switch($errno) {
case E_ERROR: case E_ERROR:
@ -451,10 +554,27 @@ function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) {
} }
/** /**
* Interface for rendering a debug info report. * Interface for stylish rendering of a debug info report.
*
*/ */
class DebugReporter { interface DebugReporter {
/**
* Render HTML markup for the header/top segment of debug report.
*/
abstract function writeHeader();
/**
* Render HTML markup for the footer and closing tags of debug report.
*/
abstract function writeFooter();
}
/**
* Concrete class to render a Sapphire specific wrapper design
* for developer errors, task runner, and test runner.
*/
class SapphireDebugReporter implements DebugReporter {
function writeHeader() { function writeHeader() {
echo '<!DOCTYPE html><html><head><title>'. $_SERVER['REQUEST_METHOD'] . ' ' .$_SERVER['REQUEST_URI'] .'</title>'; echo '<!DOCTYPE html><html><head><title>'. $_SERVER['REQUEST_METHOD'] . ' ' .$_SERVER['REQUEST_URI'] .'</title>';
@ -481,4 +601,4 @@ class DebugReporter {
} }
?> ?>