Hi guys,
I believe that everyone is well aware of errors and exceptions in
PHP. And I am sure, everyone must have their own way of handling them.
But
there is problem in PHP. PHP can easily trap and log runtime errors but
PHP error handlers cannot trap fatal errors. And it makes the debugging
a troublesome job. I too have faced problems. So, I decided to solve
this issue. After going through
PHP Manual for hours I found a method called
register_shutdown_function() I wrote a class ErrorLogging which catch fatal errors along with
notice, warnings and exception. I published it on PHPClasses.org. Here
is the link http://www.phpclasses.org/package/6512-PHP-Handle-PHP-fatal-and-non-fatal-execution-errors.html . I won first prize for this. :D (Yay!!!). This class will show you the stack-trace of errors/exceptions.
The idea.
Registers an error handler that is capable of a backtrace
with the list of functions and arguments used to call the code that
causes an error, send that information to the current page output or the
PHP error log, or send an e-mail message to the administrator. The
class can also trap fatal errors using a special PHP shutdown callback
function.
The code.
class.ErrorHandler.php.
<?php
/**
* This class handles and logs the error that occurs in the project. Exceptions will also be caught by this class.
*
* @package
* @author Nitesh Apte
* @copyright 2011
* @version 1.1
* @access private
*/
include_once 'core.php';
class ErrorHandler {
private static $_singleInstance;
/**
* @var $_backTrace Backtrace message in _customError() method
* @see _customError
*/
private $_backTrace;
/**
* @var $_errorMessage Error message
* @see _customError
*/
private $_errorMessage;
/**
* @var $_traceMessage Contains the backtrace message from _debugBacktrace() method
* @see _debugBacktrace()
*/
private $_traceMessage = '';
/**
* @var $MAXLENGTH Maximum length for backtrace message
* @see _debugBacktrace()
*/
private $_MAXLENGTH = 64;
/**
* @var $_traceArray Contains from debug_backtrace()
* @see _debugBacktrace()
*/
private $_traceArray;
/**
* @var $_defineTabs
*/
private $_defineTabs;
/**
* @var $_argsDefine
*/
private $_argsDefine = array();
/**
* @var $_newArray
*/
private $_newArray;
/**
* @var $_newValue
*/
private $_newValue;
/**
* @var $_stringValue
*/
private $_stringValue;
/**
* @var $_lineNumber
*/
private $_lineNumber;
/**
* @var $_fileName
*/
private $_fileName;
/**
* @var $_lastError
*/
private $_lastError;
/**
* Create the single instance of class
*
* @param none
* @return Object self::$_singleInstance Instance
*/
public static function _getInstance($_requestFrom) {
if(!self::$_singleInstance instanceof self) {
self::$_singleInstance = new self($_requestFrom);
}
return self::$_singleInstance;
}
/**
* Set custom error handler
*
* @param none
* @return none
*/
private function __construct($_requestFrom) {
if($_requestFrom == 'web') {
define('WEB', TRUE);
}
if($_requestFrom == 'device') {
define('DEVICE', TRUE);
}
if($_requestFrom == 'webservice') {
define('WEBSERVICE', TRUE);
}
error_reporting(1);
set_error_handler(array($this,'_customError'), APP_ERROR);
register_shutdown_function(array($this, '_fatalError'));
}
/**
* Custom error logging in custom format
*
* @param Int $errNo Error number
* @param String $errStr Error string
* @param String $errFile Error file
* @param Int $errLine Error line
* @return none
*/
public function _customError($errNo, $errStr, $errFile, $errLine) {
if(error_reporting() == 0) {
return;
}
$this->_backTrace = $this->_debugBacktrace(2);
$this->_errorMessage = "\n<h1>Website Generic Error!</h1>";
$this->_errorMessage .= "\n<b>ERROR NO : </b><font color='red'>{$errNo}</font>";
$this->_errorMessage .= "\n<b>TEXT : </b><font color='red'>{$errStr}</font>";
$this->_errorMessage .= "\n<b>LOCATION : </b><font color='red'>{$errFile}</font>, <b>line</b> {$errLine}, at ".date("F j, Y, g:i a");
$this->_errorMessage .= "\n<b>Showing Backtrace : </b>\n{$this->_backTrace} \n\n";
if(SEND_ERROR_MAIL == TRUE) {
error_log($this->_errorMessage, 1, ADMIN_ERROR_MAIL, "From: ".SEND_ERROR_FROM."\r\nTo: ".ADMIN_ERROR_MAIL);
}
if(ERROR_LOGGING==TRUE) {
if(WEB == TRUE) {
error_log($this->_errorMessage, 3, ERROR_LOGGING_FILE_WEB);
}
if(DEVICE == TRUE) {
error_log($this->_errorMessage, 3, ERROR_LOGGING_FILE_DEVICE);
}
if(WEBSERVICE == TRUE) {
error_log($this->_errorMessage, 3, ERROR_LOGGING_FILE_WEBSERVICE);
}
}
if(DEBUGGING == TRUE) {
echo "<pre>".$this->_errorMessage."</pre>";
} else {
echo SITE_GENERIC_ERROR_MSG;
}
exit;
}
/**
* Build backtrace message
*
* @param $_entriesMade Irrelevant entries in debug_backtrace, first two characters
* @return
*/
private function _debugBacktrace($_entriesMade) {
$this->_traceArray = debug_backtrace();
for($i=0;$i<$_entriesMade;$i++) {
array_shift($this->_traceArray);
}
$this->_defineTabs = sizeof($this->_traceArray)-1;
foreach($this->_traceArray as $this->_newArray) {
$this->_defineTabs -=1;
if(isset($this->_newArray['class'])) {
$this->_traceMessage .= $this->_newArray['class'].'.';
}
if(!empty($this->_newArray['args'])) {
foreach($this->_newArray['args'] as $this->_newValue) {
if(is_null($this->_newValue)) {
$this->_argsDefine[] = NULL;
} elseif(is_array($this->_newValue)) {
$this->_argsDefine[] = 'Array['.sizeof($this->_newValue).']';
} elseif(is_object($this->_newValue)) {
$this->_argsDefine[] = 'Object: '.get_class($this->_newValue);
}
elseif(is_bool($this->_newValue)) {
$this->_argsDefine[] = $this->_newValue ? 'TRUE' : 'FALSE';
} else {
$this->_newValue = (string)@$this->_newValue;
$this->_stringValue = htmlspecialchars(substr($this->_newValue, 0, $this->_MAXLENGTH));
if(strlen($this->_newValue)>$this->_MAXLENGTH) {
$this->_stringValue = '...';
}
$this->_argsDefine[] = "\"".$this->_stringValue."\"";
}
}
}
$this->_traceMessage .= $this->_newArray['function'].'('.implode(',', $this->_argsDefine).')';
$this->_lineNumber = (isset($this->_newArray['line']) ? $this->_newArray['line']:"unknown");
$this->_fileName = (isset($this->_newArray['file']) ? $this->_newArray['file']:"unknown");
$this->_traceMessage .= sprintf(" # line %4d. file: %s", $this->_lineNumber, $this->_fileName, $this->_fileName);
$this->_traceMessage .= "\n";
}
return $this->_traceMessage;
}
/**
* Method to catch fatal and parse error
*
* @param none
* @return none
*/
public function _fatalError() {
$this->_lastError = error_get_last();
if($this->_lastError['type'] == 1 || $this->_lastError['type'] == 4 || $this->_lastError['type'] == 16 || $this->_lastError['type'] == 64 || $this->_lastError['type'] == 256 || $this->_lastError['type'] == 4096) {
$this->_customError($this->_lastError['type'], $this->_lastError['message'], $this->_lastError['file'], $this->_lastError['line']);
}
}
private function __clone() {
throw new Exception("Cloning is not supported in singleton class");
}
}
?>
Notice that there is core.php included in this class. This file contains the settings for emails, location for the log files.
core.php
<?php /** * Global Default values that to be used throughout the project * * @package * @author Nitesh Apte * @copyright 2011 * @version 1.0 * @access public */ /** * Web root configuration */ define('SITE_ROOT', $_SERVER['DOCUMENT_ROOT']); /** * Error Handling Values */ define('APP_ERROR', E_ALL ^ E_NOTICE); define('DEBUGGING', TRUE); define('ADMIN_ERROR_MAIL', 'administrator@portal.com'); define('SEND_ERROR_MAIL', FALSE); define('SEND_ERROR_FROM', 'errors@portal.com'); define('IS_WARNING_FATAL', TRUE); define('ERROR_LOGGING', TRUE); define('ERROR_LOGGING_FILE_WEB', SITE_ROOT.'/logs/web/logs.ErrorWeb.log'); define('ERROR_LOGGING_FILE_WEBSERVICE', SITE_ROOT.'/logs/webservice/logs.ErrorWebService.log'); define('ERROR_LOGGING_FILE_DEVICE', SITE_ROOT.'/logs/mobile/logs.ErrorDevice.log'); define('SITE_GENERIC_ERROR_MSG', '<h1>Portal Error!</h1>'); ?> |
|
Now the usage: header.php. Just put it at the top. Thats it. You are good now.
<?php include_once "class.ErrorHandler.php"; ErrorHandler::_getInstance("web"); ?> |
|
I know you guys must be wondering what is web in parameter. It has
been observed that a website is also accessed via mobile. And sometimes
web services are created to expose services. So, here web means if
website is accessed directly then a separate error log file will be
created for it, in the location configured in core.php file. So, in
order to use this class for logging error when accessed through mobile
or web services or any other thing, make an entry in core.php for it.
And in case you are using any other thing, you will need to edit the
below section in code.
if(ERROR_LOGGING==TRUE) {
if(WEB == TRUE) {
error_log($this->_errorMessage, 3, ERROR_LOGGING_FILE_WEB);
}
if(DEVICE == TRUE) {
error_log($this->_errorMessage, 3, ERROR_LOGGING_FILE_DEVICE);
}
if(WEBSERVICE == TRUE) {
error_log($this->_errorMessage, 3, ERROR_LOGGING_FILE_WEBSERVICE);
}
}
Thats it guys.
Critics/suggestions are very much welcome.
Have a nice day ahead.