Error Exception Klasse
Error exception class
Attached an Exception-Extension similar to ErrorException. It contains one static control
function ::enable()
and the required redirections to catch errors. The class has cross-references
to the Tracer
I implemented, but checks if the tracer class exists, so the tracer is not
required per se. Unfortunately not all errors are redirected by PHP (a general problem) and
hence not all catchable. Here a sample code how to use it. The class is part of the swlib and
automatically enabled (by default autoloaded) when the main library class is included.
In the ::enable()
function the catchable errors are redirected to the ::errorCallback()
, which decide
if an error shall be only logged in the tracer (vor warnings, notices and assertions) or an exception shall
be thrown. Uncaught exceptions are delegated to uncaughtException()
, which causes a tracing and stops
the script. Assertions are handled as well.
Sample source code
Anbei eine Exception-Erweiterung ähnlich ErrorException. Sie enthält eine statische
Steuerfunktion ::enable()
und alle benötigten Einstellungen, um Fehler abzufangen.
Die Klasse kann eng mit dem von mir implementierten Tracer
zusammenarbeiten, jedoch
ist dies nicht zwingend (es wird geprüft, ob die Tracer-Klasse existiert). Leider hat
PHP das generelle Problem, dass nicht alle Fehler an die Callback weitergeleitet werden,
daher kann die Klasse nicht alle Fehler abfangen. Hier noch ein Quelltext, aus dem
die Anwendung klar wird. Die Klasse ist in der swlib enthalten und beim Start
automatisch geladen.
In der Funktion ::enable()
werden abfangbare Fehler in the ::errorCallback()
deligiert, welche dann
entscheidet, ob dieser lediglich im Tracer mitgeschrieben (Warnungen, Anmerkungen, Assertions) oder eine
Exception geworfen wird. Exceptions, welche nicht mit try{} catch() {}
abgefangen werden, werden dann
von der Funktion uncaughtException()
behandelt. Diese schreibt die Exception im Tracer mit und beendet
das Skript. Auch Assertions werden in dieser Klasse behandelt.
Anwendungsbeispiel
<?php
include_once('swlib/swlib.class.php');
// Error handler and Uncaught exception hanler is already active
// after you included the swlib main file (swlib.class.php)
use sw\Tracer;
// Causes the tracer to append the logs.
Tracer::setLevel(LOG_DEBUG);
Tracer::appendProtocol(true);
print "<html><body><pre>";
trigger_error('This should only be seen in the tracer', E_USER_NOTICE);
trigger_error('This should only be seen in the tracer', E_USER_WARNING);
try {
trigger_error('This should be catched', E_USER_ERROR);
} catch(Exception $e) {
print "<b>Was caught: </b>" . $e->getMessage();
}
// This will be caught by the exception handler.
trigger_error('This should be catched', E_USER_ERROR);
print "</pre></body></html>";
?>
Class source code
Klassen-Quelltext
<?php
/**
* Implements global error, assertion and exception handling. Errors, warnings
* and messages are categorized and either thrown as exception or only traced
* (e.g. warnings, messagses). A global exception handler catches uncaught
* exceptions, traces the details and prints a HTML error text (without details,
* as a MySqlException('You have an error near SELECT * form users where password=...')
* is nothing to be seen by the user. Assertions are only traced.
* @gpackage de.atwillys.sw.php.swLib
* @author Stefan Wilhelm
* @copyright Stefan Wilhelm, 2006-2010
* @license GPL
* @version 1.0
* @uses Exception
* @uses (optional) Tracer
*/
namespace sw;
class EException extends \Exception {
/**
* Defines if an exception shall be thrown if include() fails
* @var bool
*/
private static $config = array(
'error_levels' => array(
E_ERROR => array('level' => E_ERROR, 'tag' => 'Error', 'tracelevel' => 0),
E_USER_ERROR => array('level' => E_ERROR, 'tag' => 'User error', 'tracelevel' => 0),
E_RECOVERABLE_ERROR => array('level' => E_ERROR, 'tag' => 'Recoverable error', 'tracelevel' => 0),
E_PARSE => array('level' => E_ERROR, 'tag' => 'Parse error', 'tracelevel' => 0),
E_CORE_ERROR => array('level' => E_ERROR, 'tag' => 'Core error', 'tracelevel' => 0),
E_COMPILE_ERROR => array('level' => E_ERROR, 'tag' => 'Compile error', 'tracelevel' => 0),
E_WARNING => array('level' => E_WARNING, 'tag' => 'Warning', 'tracelevel' => 3),
E_CORE_WARNING => array('level' => E_WARNING, 'tag' => 'Core warning', 'tracelevel' => 3),
E_USER_WARNING => array('level' => E_WARNING, 'tag' => 'User warning', 'tracelevel' => 3),
E_COMPILE_WARNING => array('level' => E_WARNING, 'tag' => 'Compile warning', 'tracelevel' => 3),
E_NOTICE => array('level' => E_NOTICE, 'tag' => 'Notice', 'tracelevel' => 5),
E_USER_NOTICE => array('level' => E_NOTICE, 'tag' => 'User notice', 'tracelevel' => 5),
E_STRICT => array('level' => E_NOTICE, 'tag' => 'Strict', 'tracelevel' => 7),
E_DEPRECATED => array('level' => E_NOTICE, 'tag' => 'Deprecated', 'tracelevel' => 7),
E_USER_DEPRECATED => array('level' => E_NOTICE, 'tag' => '(User) Deprecated', 'tracelevel' => 7),
),
'throw_on_include_fail' => false,
'throw_on_division_by_zero' => true,
'warning_level' => null,
'notice_level' => null,
'mask_uncaught_exceptions' => false
);
/**
* Class configuration, first parameter can be either the config array
* (to overwrite the whole config) or a key in the existing config array.
* In the ladder case, the $value will be set for this key.
* @param array $config
* @param mixed $value
*/
public static function config($config=array(), $value=null) {
if (is_array($config)) {
if (isset($config['error_levels'])) {
$levels = array_merge(self::$config['error_levels'], $config['error_levels']);
} else {
$levels = self::$config['error_levels'];
}
self::$config = array_merge(self::$config, $config);
self::$config['error_levels'] = $levels;
} else if ($config == 'warning_level' && is_numeric($value)) {
$c = intval($value);
foreach (self::$config['error_levels'] as $key => $value) {
if ($value['level'] == E_WARNING) {
self::$config['error_levels'][$key]['tracelevel'] = $c;
}
}
} else if ($config == 'notice_level' && is_numeric($value)) {
$c = intval($value);
foreach (self::$config['error_levels'] as $key => $value) {
if ($value['level'] == E_NOTICE) {
self::$config['error_levels'][$key]['tracelevel'] = $c;
}
}
}
}
/**
* Generate en eexception if a global error is raised. Dependent on PHP, not
* all errors will be passed to this callback, but for future these cases
* are implemented.
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param string $errline
* @param string $context
* @return void
*/
public static function errorCallback($errno, $errstr, $errfile, $errline, $context) {
$errfile = str_replace($_SERVER['DOCUMENT_ROOT'], '', $errfile);
$e = "";
if (isset(self::$config['error_levels'][$errno])) {
if (self::$config['error_levels'][$errno]['level'] == E_ERROR) {
$e = self::$config['error_levels'][$errno]['tag'];
} else if ($errno == E_WARNING) {
$etxt = strtolower(trim($errstr));
if (strpos($etxt, 'function.include') !== false && self::$config['throw_on_include_fail']) {
$errno = E_ERROR;
$e = "Error:";
$errstr = "Include failed";
} else if (strpos($etxt, 'function.require') !== false) {
$errno = E_ERROR;
$e = "Error:";
$errstr = "Require failed";
} else if (strpos($etxt, 'division by zero') !== false && self::$config['throw_on_division_by_zero']) {
$errno = E_ERROR;
$e = "Error:";
$errstr = "Division by zero";
$eclass = '\sw\MathException';
}
}
if (class_exists('sw\\Tracer', false)) {
Tracer::trace(self::$config['error_levels'][$errno]['tag'] . ": $errstr ($errfile@$errline)", self::$config['error_levels'][$errno]['tracelevel']);
}
if ($e != '') {
$e = isset($eclass) ? new $eclass() : new self();
$e->line = $errline;
$e->file = $errfile;
$e->message = strip_tags($errstr);
$e->code = $errno;
throw $e;
}
}
}
/**
* Global catches uncaught exceptions
* @param Exception $e
* @return void
*/
public static function uncaughtException(\Exception $e) {
if(swlib::isCLIMode()) {
while(@ob_get_level() > 0) @ob_end_flush();
@file_put_contents('php://stderr', "\n\033[31mUncaught Exception: '" . $e->getMessage() . "' in .../" . basename(dirname($e->getFile())) . "/" . basename($e->getFile()) . "[" . $e->getLine() . "]\033[30m\n", FILE_APPEND);
if(class_exists('sw\\Tracer', false)) {
@file_put_contents('php://stderr', "\033[35m\n" . Tracer::backtrace($e->getTrace(), true) . "\033[30m\n", FILE_APPEND);
}
die;
} else if(self::$config['mask_uncaught_exceptions']) {
print "<div style='padding:5px; border: solid 1px black; background-color:#ff8888;' class=\"uncaught-exception\">A server error occoured. <br /><i>There is an uncaught exception, which is not visible to the user and has to be handled by the developer.</i></div>";
} else {
print "Uncaught Exception: " . $e->getMessage();
}
if(class_exists('sw\\Tracer', false)) {
Tracer::traceUncaughtException($e);
}
die;
}
/**
* Callback for usage of function assert()
* @param string $file
* @param int $line
* @param string $code
*/
public static function assertCallback($file, $line, $code='') {
if (class_exists('sw\\Tracer', false)) {
Tracer::trace("Assertion failed in '$file'@$line: $code");
}
}
/**
* Initialize and enable class usage.
* @param bool $enabled
* @return void
*/
public static function enable($enabled=true) {
assert_options(ASSERT_ACTIVE, 1);
assert_options(ASSERT_WARNING, 0);
assert_options(ASSERT_QUIET_EVAL, 1);
restore_error_handler();
restore_exception_handler();
if ($enabled) {
assert_options(ASSERT_CALLBACK, array(__CLASS__, "assertCallback"));
set_exception_handler(array(__CLASS__, "uncaughtException"));
set_error_handler(array(__CLASS__, "errorCallback"));
}
}
}