<?
/**
* Generic error reporting and handling class.
*
* xError is a generic error reporting and handling class written in PHP. It may be used for development and/or monitoring purposes.
*
* @version 0.1
* @author Alexander Serbe <alex@onlinetools.org>
* @copyright Copyright © 2003 by Alexander Serbe. All rights reserved.
* @license http://www.gnu.org/licenses/gpl.html GNU General Public License (GPL)
*/
class xError
{
/**
* @var array $definitions Error definitions.
* @access private
*/
var $definitions;
/**
* @var array errorStack Error stack. Errors (non-fatal) that occurred during the execution of the script.
* @access private
*/
var $errorStack;
/**
* @var string $errorLog The error log file.
* @access private
*/
var $errorLog;
/**
* @var boolean $logFatalErrors Determine whether to log fatal errors or not.
* @access private
*/
var $logFatalErrors;
/**
* @var string $tmpFatal Template for fatal errors.
* @access private
*/
var $tmpFatal;
/**
* @var string $tmpNonFatal Template for non-fatal errors.
* @access private
*/
var $tmpNonFatal;
/**
* Initializes a new xError object.
* @access public
* @return void
*/
function xError()
{
// Initialize object variables...
$this->definitions = array();
$this->errorStack = array();
$this->errorLog = '';
$this->tmpFatal = '';
$this->tmpNonFatal = '';
$this->logFatalErrors ( false );
// Start output buffering
ob_start();
}
/**
* Checks if the passed error code exists in the list.
* @access private
* @return boolean
* @param string $code The error code to be checked.
*/
function checkErrorCode ( $code )
{
if ( count ( $this->errorDefinitions ) <= 0 )
return false;
foreach ( $this->errorDefinitions as $error )
{
if ( $error['code'] == $code )
return true;
}
return false;
}
/**
* Displays a fatal error.
* @access private
* @return boolean
* @param string $code Error code.
*/
function displayFatalError ( $code )
{
// Check if error code exists
if ( !$this->checkErrorCode ( $code ) )
return false;
// Get the error definition
$x = $this->getErrorDefinition ( $code );
// Check if error is really 'fatal'
if ( $x['type'] != 'fatal' )
return false;
ob_clean();
// Get exact time
$yA = explode ( " ", microtime() );
$ms = $yA[0];
$time = $yA[1];
$yB = date ( 'd M Y H:i:s', $time );
$yC = $yB . substr ( $ms, 1, 7 );
if ( $this->tmpFatal )
{
$fp = fopen ( $this->tmpFatal, "r" );
$tmp = fread ( $fp, filesize ( $this->tmpFatal ) );
fclose ( $fp );
$errorMessage = "<b>$yC:</b> <i>" . $x['code'] . "</i> - " . $x['user'] . "<br /><br />" . $x['suggestions'];
$tmp = str_replace ( "<!-- error_messages -->", $errorMessage, $tmp );
echo $tmp;
exit;
}
return false;
}
/**
* Displays non-fatal errors.
* @access public
* @return boolean
*/
function displayNonFatalError()
{
ob_clean();
if ( $this->tmpNonFatal )
{
$fp = fopen ( $this->tmpNonFatal, "r" );
$tmp = fread ( $fp, filesize ( $this->tmpNonFatal ) );
fclose ( $fp );
$errorMsg = '<table border="0" cellpadding="3" cellspacing="0">';
$errorMsg .= '<tr>';
$errorMsg .= '<td valign="top"><b>Time</b></td>';
$errorMsg .= '<td valign="top"><b>Error Code</b></td>';
$errorMsg .= '<td valign="top"><b>Error /<br />Suggestions</b></td>';
$errorMsg .= '</tr>';
$errorMsg .= '<tr><td colspan="3"><hr noshade size="1" /></td></tr>';
for ( $i=0; $i<count ( $this->errorStack ); $i++ )
{
$cnt = $i + 1;
$error = $this->errorStack[$i];
$errorMsg .= '<tr>';
$errorMsg .= '<td valign="top">' . $error['time'] . '</td>';
$errorMsg .= '<td align="center" valign="top">' . $error['code'] . '</td>';
$errorMsg .= '<td valign="top">' . $error['user'];
if ( strlen ( $error['suggestions'] ) > 0 )
$errorMsg .= '<br /><i>' . $error['suggestions'] . '</i>';
$errorMsg .= '</td>';
$errorMsg .= '</tr>';
$errorMsg .= '<tr><td colspan="3"><hr noshade size="1" /></td></tr>';
}
$tmp = str_replace ( "<!-- error_messages -->", $errorMsg, $tmp );
echo $tmp;
exit;
}
return false;
}
/**
* Clears the error stack and returns its contents. Stops the output buffering.
* @access public
* @return array
*/
function flushErrors()
{
// Store the contents of the error stack in a temporary array
$x = $this->errorStack;
// Empty error stack
$this->errorStack = array();
// Return error stack
return $x;
}
/**
* Returns an error from the error stack.
* @access private
* @return array
* @param integer $element Element number.
*/
function getError ( $element )
{
// Check if error stack has elements
if ( count ( $this->errorStack ) <= 0 )
return false;
if ( is_array ( $this->errorStack[$element] ) )
return $this->errorStack[$element];
return false;
}
/**
* Returns an error definition.
* @access private
* @return array
* @param string $code The error code to be returned.
*/
function getErrorDefinition ( $code )
{
// Check if code exists
if ( !$this->checkErrorCode ( $code ) )
return false;
foreach ( $this->errorDefinitions as $error )
{
if ( $error['code'] == $code )
return $error;
}
return false;
}
/**
* Imports error definitions from a text file.
* @access public
* @return boolean
* @param string $file The text file, the error definitions reside in.
*/
function importDefinitions ( $file )
{
// Check if passed file exists an is not empty
if ( !file_exists ( $file ) || filesize ( $file ) <= 0 )
return false;
// Initialize counters
$cnt = 0;
$lineCnt = 0;
// Open file
$fp = fopen ( $file, "r" );
// Loop through file content and extract error definitions
while ( !feof ( $fp ) )
{
$lineCnt++;
$line = fgets ( $fp );
$x = split ( ";", $line );
// Check if the error code exists already in the error list
$y = $this->setError ( $x[0], $x[1], $x[2], $x[3], $x[4] );
( $y ) ? $cnt++ : $cnt = $cnt;
}
// Close file
fclose ( $fp );
return ( $cnt != $lineCnt ) ? false : true;
}
/**
* Determine whether fatal errors should be logged or not. (Log file has to be specified manually using the setLogFile method!)
* @access public
* @return boolean
* @param boolean $flag True = log fatal errors; false = do not log fatal errors.
*/
function logFatalErrors ( $flag )
{
if ( !is_bool ( $flag ) )
return false;
$this->logFatalErrors = $flag;
return true;
}
/**
* Adds an error to the error stack.
* @access public
* @return boolean
* @param string $code Error code.
* @param string $addInfo Additional information on the error (e.g. SQL errors etc.)
*/
function raiseError ( $code, $addInfo='' )
{
// Check error code
if ( !$this->checkErrorCode ( $code ) )
return false;
// Get exact time
$x = explode ( " ", microtime() );
$ms = $x[0];
$time = $x[1];
$y = date ( 'd M Y H:i:s', $time );
$z = $y . substr ( $ms, 1, 7 );
$error = $this->getErrorDefinition ( $code );
if ( $error['type'] == 'fatal' )
{
if ( $this->logFatalErrors && $this->errorLog )
{
$fp = fopen ( $this->errorLog, "a" );
$errorString = "$z;" . $error['code'] . ";" . $error['admin'] . ";" . $error['type'] . ";$addInfo\n";
fputs ( $fp, $errorString );
fclose ( $fp );
}
$this->displayFatalError ( $code );
return true;
}
$newError = array (
'time' => $z,
'code' => $error['code'],
'user' => $error['user'],
'admin' => $error['admin'],
'type' => $error['type'],
'suggestions' => $error['suggestions'],
'info' => $addInfo
);
$this->errorStack[] = $newError;
return true;
}
/**
* Adds an error definition to the list.
* @access public
* @return boolean
* @param string $code Error code.
* @param string $userMessage The error message for the "normal" user.
* @param string $adminMessage The error message for administrators and/or developers.
* @param string $type The error type (fatal, non-fatal)
* @param string $suggestions Suggestions how to avoid the error.
*/
function setError ( $code, $userMessage, $adminMessage='', $type='fatal', $suggestions='' )
{
// Check if error code exists already in error list.
if ( $this->checkErrorCode ( $code ) )
return false;
// Check if error type is either 'fatal' or 'non-fatal'
if ( $type != 'fatal' && $type != 'non-fatal' )
return false;
$this->errorDefinitions[] = array (
'code' => $code,
'user' => $userMessage,
'admin' => $adminMessage,
'type' => $type,
'suggestions' => $suggestions
);
return true;
}
/**
* Sets the error log.
* @access public
* @return void
* @param string $file Log file.
*/
function setLogFile ( $file )
{
$this->errorLog = $file;
}
/**
* Sets the templates for the output of errors.
* @access public
* @return boolean
* @param string $fatal Template for fatal errors
* @param string $nonFatal Template for non-fatal errors
*/
function setTemplates ( $fatal, $nonFatal='' )
{
$check = false;
if ( strlen ( $fatal ) > 0 && file_exists ( $fatal ) )
{
$check = true;
$this->tmpFatal = $fatal;
}
if ( strlen ( $nonFatal ) > 0 && file_exists ( $nonFatal ) )
{
$check = false;
$this->tmpNonFatal = $nonFatal;
}
return $check;
}
/**
* Writes the contents of the error stack into a log file.
* @access public
* @return boolean
*/
function writeLog()
{
// Check if any errors were recorded
if ( count ( $this->errorStack ) <= 0 )
return false;
$fp = fopen ( $this->errorLog, "a" );
for ( $i=0; $i<count ( $this->errorStack ); $i++ )
{
$x = $this->getError ( $i );
$logString = $x['time'] . ";" . $x['code'] . ";" . $x['admin'] . ";" . $x['type'] . ";" . $x['info'] . "\n";
fputs ( $fp, $logString );
}
fclose ( $fp );
}
}
// Initialize a new xError object...
$x = new xError();
// Import error definitions...
$x->importDefinitions ( "./errors01.txt" );
// Set the templates for fatal and non-fatal errors...
$x->setTemplates ( "./fatal.html", "./non-fatal.html" );
// Set the error log...
$x->setLogFile ( "./error_log.txt" );
// Yes, we do log fatal errors...
$x->logFatalErrors ( true );
// The contents shown, if no fatal error occurs...
echo "If you see this message, no fatal errors occurred...";
// Raise some errors (uncomment the last one to see how a fatal error is handled
$x->raiseError ( "x100" );
$x->raiseError ( "x109" );
$x->raiseError ( "x105" );
$x->raiseError ( "x106" );
//$x->raiseError ( "x200" );
// Write the contents of the error stack (non-fatal errors only, since no fatal
// error has occurred if the script got executed to this point...
$x->writeLog();
// Clean-up the error stack and return it's content...
$y = $x->flushErrors();
// Check if any errors occurred...
echo ( count ( $y ) > 0 ) ? "<hr />However, some <b>non-fatal</b> errors did occur:<br /><br />" : "";
// Display an error message containing the errors from the error stack...
// (Optionally you can use the 'displayNonFatalError' method to display non-fatal errors)
foreach ( $y as $error )
{
echo $error['time'] . " - <b>" . $error['code'] . "</b> : " . $error['user'];
echo ( $error['suggestions'] ) ? "<br /><b>Suggestions:</b> <i>" . $error['suggestions'] . "</i>" : "";
echo "<br /><br />";
}
echo "<hr />";
?>