Debugging is essentially of a way of life. Debugging keeps you sane, and provides you with peace of mind. Debugging makes it so that you don’t have to pull your hair out when something does not work. Debugging is the most direct way of finding problems in your code, and it’s exactly what I hope to help you learn to do right now.
It seems to me that, at some point in every programmer’s life, they finally get realistic, and say “That’s it… I’m not going to second guess my logic anymore. I’m going to make this stupid thing debug itself!”. I would guess that this was first said long ago, shortly after the advent of the punch card, when thousands of cards made up a simple TicTacToe computer game. I don’t think that it’s really something that programmers ever pass down to their disciples either. Throughout time it’s been something that each programmer would stumble on in their own time, and would be a milestone in their logical development as a programmer.
Building a program is a lot like playing with Legos (TM). When I was little I would build towers and castles and anything else my little imagination could come up with. Inevitably, some of them fell down, but I could easily learn from my mistakes. I could see which way the tower fell, and tell where each block was stuck together. I’m sure that most of us don’t have the capacity to imagine our programs as such – not with the detail needed to debug, at least. And that is why, when our programs fail, we are left wading through a murky puddle of half-thought-out logic, incomplete ideas, and caffeine stained notes from 4 months ago. So we have to find a way to debug.
Going about debugging a program is, actually, simpler then one might imagine. OK, to be fair, there are complicated ways of debugging but, since this is a tutorial for beginners, we’ll stick to the boneheaded basics (not that there is anything wrong with the boneheaded basics, mind you!).
Methods for debugging your code follow a couple of very distinct paths, each with its pro’s and its con’s. We have the poor man’s Debug-By-Echo, the poor man’s Debug-By- Logfile, and the poor man’s Debug-By-errorTrap. These, by the way, are just my names for the methods, not standard programming names.
Short descriptions of the three are as follows (I’ve started abbreviating them for the sake of brevity.):
DBE (Debug-By-Echo) is as simple as debugging methods come. All you are doing is spouting off information which, hopefully, is meaningful to the programmer.
DBL (Debug-By-Logging) is basically the same thing as DBE, except that it writes to a file.
DBT (Debug-By-errorTrap) is the most complicated of the three. It displays only relevant information based on checking the data we DO have versus the data that we SHOULD have. It can be the most useful, but takes more time and thought to implement.
DBE (Debug-By-Echo)
DBE is probably the simplest of the debugging methods (which often makes it the most effective!). DBE is an extremely easily implemented method which can tell you EXACTLY where something went wrong. It’s done by simply echoing some text between the lines, including the variables you are currently working with. Here’s an example:
<?php
function doStuff($num) {
if ( is_numeric($num) ) {
echo ‘[1]: Doung stuff ‘ . $num . ‘ times<br>’;
}
else {
die (‘[1]: doStuff() called with a non integer<br>’);
}
echo ‘[1]: Begining execution loop<br>’;
$count = 0;
while ( $count < $num ) {
$count++;
echo ‘[1]: Stuff, loop #’ . $count . ‘<br>’;
// do more stuff later on…
}
echo ‘[1]: Finished execution loop<br>’;
Return True;
}
echo ‘[0]: Begining program execution<br>’;
doStuff(7);
echo ‘[0]: Ending program execution<br>’;
?>
This example fully illustrates how to take advantage of DBE. We have a marker representing where we are in the program. In this instance I used [0] to signify that we were at the root of the code. [1] means that we are inside function one. And if I had more functions they would be [2], [3], [4], and so on. This allows us to quickly reference which part of the code is being processed.
We also output the values of all of the variables that are being worked within our loop inside dostuff(). Now, if we had a simple flaw in our logic and our while loop was not going through enough iterations, or things were happening in the wrong order, this output makes most problems easy to track down.
DBL (Debug-By-Logging)
DBL is much the same, except that instead of echo’ing, you log the information to a file (with simple fopen() and fputs() statements. The beauty of this method is that you can keep permanent records, and compare their behavior.
Let’s say something started happening in version 1.2 of a script you were writing, but did not happen in 1.1. Using DBL method you can simply look at the log files created when debugging, and find where the two versions started to disagree. This is not always true for all circumstances, but can be a very useful tool when used properly.
Now, if you are writing code to be distributed, it is sometimes desirable to allow the user to change a simple variable to turn debugging output on or off. You might have them e-mail the output you (or let you see it) which will help troubleshoot problems that other people are having with your code… you can do that quite simply like this:
<?php
// configuration Variables
// set to ‘0’ to disable debugging messages
$debug = 1;
$logfile = ‘./program.log’;
function logentry($line) {
global $logfile;
// open our log file
$fp = @fopen($logfile, ‘a’);
//check to make sure it’s useable now…
if ( $fp ) {
// if it is – write our line, close the file,
// exit the function we’re also going to time
// stamp is so that we can see when something
// happened (useful if the program is being
// looked at for lack of speed)
fputs($fp, ‘[‘.time().‘] ‘ . $line . chr(10));
fclose($fp);
return True;
}
else {
// if not, then something is wrong…
die(‘FATAL ERROR: COULD NOT OPEN LOG FILE!’ . chr(10));
}
}
function doStuff($num) {
global $logfile, $debug;
if ( is_numeric($num) ) {
if ( $debug == 1 ) {
logentry(‘[1]: Doung stuff ‘ . $num . ‘ times’);
}
}
else {
logentry(‘[1]: doStuff() called with a non integer’);
die();
}
if ( $debug == 1 ) {
logentry(‘[1]: Beginning execution loop’);
}
$count = 0;
while ( $count < $num ) {
$count++;
if ( $debug == 1 ) {
logentry(‘[1]: Stuff, loop #’ .$count);
}
// do more stuff later on…
}
if ( $debug == 1 ) {
logentry(‘[1]: Finished execution loop’);
}
Return True;
}
if ( $debug == 1 ) {
logentry(‘[0]: Beginning program execution’);
}
doStuff(7);
if ( $debug == 1 ) {
logentry(‘[0]: Ending program execution’);
}
?>
It adds quite a few lines to your code, but it’s an excellent tool when you need to troubleshoot. You might consider removing the debugging from the final version to speed things up if performance is too much of an issue.
DBT (Debug-By-errorTrap)
DBT is the least common, and most complex type of debugging. It is similar to DBE (or DBL if you are logging), but incorporates a bit of intelligence – which is nice. Let’s say you have the following code:
<?php
$data=@file('http://blah.com/index.shtml');
foreach ( $data as $line ) {
echo $line;
}
?>
Now, file() returns you an array if successful, and foreach() expects an array, but what if you call a url or file that does not exist, or that you cannot open? Then you get an error saying “Warning: Invalid argument supplied for foreach() in /whatever/file/it/could/happen.php on line 4″. This is all fine and good, but if you’re interested in making more sense out of things, or just want to make the error look better for a client, or maybe you expect this to fail sometimes, this is when you use error trapping, like so:
<?php
$data=@file('http://blah.com/index.shtml');
if ( is_array($data) ) {
foreach ( $data as $line ) {
echo $line;
}
}
else {
echo 'File Not Found!';
}
?>
In this example we have thought all of the possibilities through, and provided the program with a GRACEFUL way of handling any problems. I’ve included this as a debugging technique, but realistically it’s just nothing but good programming practice. If something can go wrong, then you – as a programmer – should expect that it will, and provide for that in DBT.
In a perfect world, debugging would not be necessary (because programs would run correctly the first time) and my refrigerator would never run out of Pepsi for me to drink! If you insist on living in this world, plan on going bald a lot sooner, and renting a room with padded walls, because finding a problem without a little bit of troubleshooting is bound to drive even the most learned ‘guru’ insane!
-Demitrious
Home page: http://www.apokalyptik.com/
I’m also available for *nix and php consulting, e-mail [email protected] if interested. Cheers!