Friday, February 02, 2007

Trapping Fatal Errors in PHP

Generally most of PHP programmers feels error handling in PHP is cumbersome or they think its just not very programmer friendly and impossible to handle fatal errors. In PHP 4.X.X versions or you can say PHP versions less that PHP 5, its possible to trap the errors with functions like set_error_handler() and trigger_error(). But the possible errors that can be handled by PHP 4 are just Warnings and Notices.

Many programmers have tried to find out ways to handle fatal errors that occur in a program, but never had patience or time to find out a viable solution. Fortunately I encountered a problem in that I must figure out some ways to handle fatal errors in PHP 4 in order to fix the issue. When I started working on this task, I looked all around globe(net) for some clues about how to handle fatal errors, but mostly all of them are either incomplete or not just straight forward that can be understandable instead thay used all sort of tech jerks to make the mud more murkier.

So here I am going to give a clear picture about how to handle fatal errors in PHP 4 with complete lab tested code that will actually work fine in all type of systems.

The main concept behind the logic I used to trap the fatal errors are output buffering. Output buffering is itself a important and big topic to discuss, and if we get into that then we will miss out from the track of our actual destination of trapping errors. So we can move on with first hand information about the output buffering.

The Output Control functions in PHP like ob_start() allows you to control when output is sent from the script. This can be used to control when a script output should be send back to browser.

Okay now I like to go straight into code and later on explain the basics.

FileName1: ErrorHandlerLib.php

<?php
//program to handle fatal errors in PHP 4

//set the error handler to catch unexpected errors
set_error_handler('LowCategoryErrors');
//start the buffering to catch the fatal error msgs
ob_start('FatalErrors');



/**
* function to handle different error types raised by PHP
*
* @return void
*/
function LowCategoryErrors($argErrNr, $argErrMsg, $argFileName, $argLineNr)
{
//initialize local variables
$szErrType = null;
$aErrorType = array(
E_ERROR => 'Error',
E_WARNING => 'Warning',
E_PARSE => 'Parser Error',
E_NOTICE => 'Notice',
E_CORE_ERROR => 'Core Error',
E_CORE_WARNING => 'Core Warning',
E_COMPILE_ERROR => 'Compile Error',
E_COMPILE_WARNING => 'Compile Warning',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
);

//check we have value or not, else set default
if(array_key_exists($argErrNr, $aErrorType))
$szErrType = $aErrorType[$argErrNr];
else
$szErrType = "Unknown";

//form the error message string
$argErrMsg = "Caught PHP Error. $argErrMsg in $argFileName at $argLineNr.";

echo "<br>$szErrType: $argErrMsg\n";
}

/**
* function to handle Fatal error type raised by PHP. This works based on output buffering.
*
* @return string
*/
function FatalErrors($argBuffer = null)
{
//intialize local variables
$aMatches = array();
$argErrMsg = $retValue = $szErrType = null;
$tRes = true;

//parse the buffer to check whether there is any error or not
$tRes = preg_match_all("/<b>(.+?)\serror<\/b>:(.*?)<br/i", $argBuffer, $aMatches,PREG_SET_ORDER);

//diff the error and 0 return value
if($tRes===false)
{
$retValue = "<br>Fatal: Failed to evaluate regex pattern while validing the output buffer.\n";
}
elseif($tRes == 0)
{
//if we dont find any error, return the buffer content as it is
$retValue = $argBuffer;
}
else
{
//loop through the reg-ex patterns that is passed through arguments
for($iCount = 0; $iCount < count($aMatches); $iCount++)
{

//form the error message string
$argErrMsg = "Caught PHP {$aMatches[$iCount][1]} Error. Error Message: {$aMatches[$iCount]['2']}.";

echo "<br>Unrecoverable Error: $argErrMsg";

$retValue = "<br>$argErrMsg";
}
}

return $retValue;
}
?>


The function set_error_handler() here is used to register the error handler for warnings and notices. This will take care of lower category.

The second function ob_start() is the real king here. This registers the PHP buffer output handler and through this we are implementing the fatal error handling logic. This function FatalErrors() will called is there is any explicit call to end the buffer or else it will called whenever the PHP script is going to complete its final processing. Remember here, this function is also called whenever PHP going to end its execution. This makes us possible to trap fatal errors and do last minute processing.

FileName2: ErrorHandlerUsage.php

<?php

//program to demostrate the usage of fatal error handler

//include the error handler library
include "ErrorHandlerLib.php";

//error_reporting should be set to show all in order to catch fatal errors.
error_reporting(E_ALL);


echo "<br>Hello world";


/******************************************************************
Section1 : this makes notices and handled nicely by a handler
******************************************************************/

echo "<br>Im going to make some notices";

//the following statement will make the notice
$TestVar = $UndefinedVariable;

//End of Section1

/******************************************************************
Section2 : this makes warnings and handled nicely by a handler
******************************************************************/
echo "<br>Im going to make some warnings";

//the following statement will make the warning
implode('test');

//End of Section2

/*

//Uncomment this Section 3 only if you want to test the Fatal error handler, to test
//other type of error, just run the program.

//*****************************************************************
//Section3 : this makes warnings and handled nicely by a handler
//******************************************************************
echo "<br>Im going to make fatal error by calling undefined function";

//the following call to undefined functions makes fatal error
SomeJunk();

//End of Section3

*/
?>


The above code example is pretty much self explanatory and clearly spells out how trapping fatal errors are easy in PHP.

All this are just an example of single usage. You can customize outputting and last minute actions according to your needs and logics. You can implement this using object oriented programming logic in different way with other set of restrictions like freezing of object attributes by PHP.

If you like to clarify anything about this or just wanted to know one oe other about PHP you can trap me at kramchel-inet +at+ yahoo +dot+ co +dot+ in(replace the words +at+ and +dot+ with @ and . characters respectively). This is secondary mail ID I use to fight against spam. You can get my permanent ID after sending a first mail.

Happy Coding.

3 comments:

  1. Thx, this is GREAT!!! I was urgently searching for a way to get notified, when Fatal Errors occur. Now I know how to do this. Thank you!

    ReplyDelete
  2. Thats great BUT it doesn't work on parse errors. Any idea of how to handle them ?

    ReplyDelete
  3. This is OK so far as it goes, but it doesn't allow you to recover from fatal errors. For example, "Call to a member function on a non-object" is fatal in PHP, but a standard exception in most other languages (e.g. NullPointerException in Java)

    ReplyDelete