Errors in PHP

“It’s been a looooong time… how have you been?”

Never played Portal 2? If not, you should surely do so ;) Ok let’s get started:

Today we will focus on writing a basic error reporting class. I know I wanted to start with a database class, but since I decided that the db class should implement error reporting, here is what we will do now:

1.) We need a general config file. What we therefore do, is create a folder “/config/” and a file in this folder “/config.inc”! Why .inc and not .php? The file is only intended for inclusion and not to be called directly!

2.) We want to have four settings:
- an E-Mail address errors get sent to
- a flag indicating whether we want E-Mails to be sent or not (useful for live / debug versions)
- an E-Mail adress that shows up as the recipient
- The max. amount of backtrace steps we need

So basically this is it:

$CONFIG['ERROR']['MAILTO'] = "webmaster@mycodeland.com";
$CONFIG['ERROR']['ENABLED'] = true;
$CONFIG['ERROR']['MAILFROM'] = "daemon@mycodeland.com";
$CONFIG['ERROR']['MAXTRACE'] = 8;

3.) Let’s code our class: We will put the file into “/class/” (not into “/config/”, but one level above that one) and call it error_reporting.class.inc!

Ok, here is the basic class, with config included and two static functions (one for building the error message, the other will take care of sending an email:

include_once("../config/config.inc");
 
class error_reporting
{
public static function reportError()
{
 
}
public static function sendErrorReport($title, $message)
{
global $CONFIG;
 
$header = "MIME-Version: 1.0\r\n";
$header .= "Content-type: text/html; charset=iso-8859-1\r\n";
$header .= 'From: '.$CONFIG['ERROR']['MAILFROM'].'r\n';
 
mail($CONFIG['ERROR']['MAILTO'], $title, $message, $header);
}
}

I will not go into the details of the “sendErrorReport()” function, for that should be pretty clear (it sends a mail ;) .

Now to the coding of the reportError():
Our secret is php’s debug_backtrace() function. This function returns a compete backtrace as array!
Now as you can see, our sendErrorReport function expects a title and a message, we will put together the title first:

public static function reportError($msg="")
{
//Load our debug info into var//
$info = debug_backtrace();
 
//And put together our title//
//The caller function is located in $info[1]
$debugtitle = "Error occurred in \"{$info[0]['file']}\" on line {$info[0]['line']}";
}

Regarding further details of the debug_backtrace() function, please rely to the php.net documentation. A short explanation of what we’re doing:

We’re storing our backtrace array in $info. The information about the page directly calling the function is stored in array key [0], the info about the page calling the function calling our backtrace is stored in key [1] and so on.. within these array keys you can access the info (which is again an array) using e.g. ['filename'] and ['line'] to get the corresponding information. Therefore our title should look something like “Error occurred in “/inc/config.inc” on line 5″.
The above code basically tells us the line, where our reportError() function is called.

Please note that I have also added a $msg parameter to our function. The coder using this function will be able to include a custom error message by himself along with our generated debug info.

Now let’s build our message:

We need the $CONFIG['error']['MAXTRACE'] value, therefore we define $CONFIG as global at the start of our function. After doing that, we write our loop that will put together the message:

//Start our error message//
//Time of error//
$debugmessage = "<strong>Time of error</strong>:".date("Y-m-d H:i:s")."
\r\n";
 
//Only add backtrace if we have more than 1 function step//
if(count($info) &gt; 1)
{
//Build backtrace//
$debugmessage .= "Backtrace:
\r\n";
 
foreach($info as $key =&gt; $step)
{
//Break if we reached max. steps defined in Config//
if($key == ($CONFIG['ERROR']['MAXTRACE']))
break;
 
//Filename, called function and line number//
$debugmessage .= "<strong>Step {$key} -&gt; Filename:</strong> ".$step['file']." ; Function called: ".$step['function']." on line ".$step['line']."
\r\n";
 
//Func. arguments if we have any://
if(!empty($step['args']))
{
$debugmessage .= "Arguments for function: ".implode(",", $step['args'])."
\r\n";
}
}
}

So what we are doing, is adding the time when the error occurred, and then (only if we have a caller function and our reportError() func was called from within another function) looping through our backtrace array (we need to stop at the MAXTRACE value defined in our config). For each trace-step, we add a line with the filename, the called function and the line number. Finally we add function arguments
as well (if there are any). The function arguments will be just comma concatenated, with might not be ideal for reading, but this will have to do for now! :)

Finally we want to include GET, POST, SESSION and COOKIE data into our bug report. We use a little trick here:
var_dump() would produce a good overview of the array data, but we can’t directly use var_dump(), because it prints the array dump instead of returning the data.
So we need this little func here to capture the output of var_dump():

/*
* Little trick function to get var_dump() output stored as string
*/
public static function getOutputBufferValue($array)
{
ob_start();
var_dump($array);
$txt = ob_get_contents();
ob_end_clean();
 
return $txt;
}

Thus said, we add the following huge codeblock to our reportError() function:

//We want to know GET, POST, SESSION and COOKIE data//
if(!empty($_GET))
{
$debugmessage .= "<strong>GET-Dump</strong>:
\r\n";
$debugmessage .= error_reporting::getOutputBufferValue($_GET);
}
 
if(!empty($_POST))
{
$debugmessage .= "<strong>POST-Dump</strong>:
\r\n";
$debugmessage .= error_reporting::getOutputBufferValue($_POST);
}
 
if(!empty($_SESSION))
{
$debugmessage .= "<strong>SESSION-Dump</strong>:
\r\n";
$debugmessage .= error_reporting::getOutputBufferValue($_SESSION);
}
 
if(!empty($_COOKIE))
{
$debugmessage .= "<strong>COOKIE-Dump</strong>:
\r\n";
$debugmessage .= error_reporting::getOutputBufferValue($_COOKIE);
}
 
//Finally add programmer's comment://
$debugmessage .= "<strong>Programmer Comment</strong>:
\r\n";
$debugmessage .= $msg;

We only add the value of GET, POST, SESSION and COOKIE to our debugmessage, and finally add the programmer’s comment!

The last part is to send our E-Mail with the prepared Subject / message :)

//Call our email func with according values :)//
error_reporting::sendErrorReport($debugtitle, $debugmessage);

That’s it, we’re done.. just add “error_reporting::reportError(“MyComment”);” on critical parts of your code (such as mysql_query() OR DIE(error_reporting::blabla:)!
I surely know, this is a huge lot what we’ve done here, and I hope I’ve explained everything well enough. If not, do not hesitate to ask for help :) !

I will publish repository and downloadable code packages (this example for now) soon, so you can have the complete class ready for download!

Here is the repository link!

And here the download link for the package (as of 17.09.2011)!

Good night :)

Leave a Reply