debugging php
Have you ever spent time tracking down a problem with your PHP code that causes it to give unexpected results? Have you ever wished for a quick and easy way to visualise exactly what's going on,
where and why? Of course you have. I have, too. That's why I made dpDebug: a single-file PHP library that 'injects' a handy pop-up list of messages defined by you into any page you like. All you need
to do is call the deb() function at each step you want to trace, with a brief description of what's happening, and it'll be added to the dpDebug message log. You can even specify message levels (fatal,
warning, notice, info, trivial, highlight or custom) and set a verbosity level to choose how much detail you want to see in the log.
Of course, dpDebug isn't meant to be active on a live website. It's easy to enable or disable depending on whichever circumstances you like:
Example: selectively enabling dpDebug
<?php
include("dpDebug2.php");
if ($dev_mode) {
set_verbosity(10);
} else {
set_verbosity(0);
}
?>
Of course, the conditions for enabling dpDebug are entirely up to you. I usually use a cookie containing a hash, set by a password-protected enable_debug.php:
Example: using a cookie to enable dpDebug
<?php
include("dpDebug2.php");
$verbosity = 0;
if (isset($_COOKIES['dpDebug_hash'])){
if ($_COOKIES['dpDebug_hash'] == md5("my dpDebug passphrase/seed" . $_SERVER["REMOTE_ADDR"])) $verbosity = 10;
}
set_verbosity($verbosity);
?>
the deb() function
deb(string Message [ , int Level [ , string Label [ , bool Code [ , array Trace ] ] ] ]);
The deb() function is the main interface between the programmer and dpDebug. It appends a message to the debug log, so you'll want to call this wherever something's happening that
you'll want to monitor when a problem arises.
It sounds like a chore to be adding deb() calls all over the place. The few people who were aware of dpDebug 1 agreed, however, that it quickly became habit, and that it was very
much worth it for the huge boost they gained in productivity and potential of complexity. If you have a lot of existing code for which it would make little sense to trawl through, adding calls to
deb() everywhere it might be useful, you'll probably find yourself adding it here and there to start with, then eventually ending up with a detailed record of what's happening.
Just try it: you might never go back! Even if you don't make regular calls to deb(), you will still find dpDebug a handy way to show and track PHP errors without breaking the flow of
your page.
Example: basic use of the deb() function
<?php
include("dpDebug2.php");
deb("Loading configuration",4); // level 4 (info) message
require("includes/config.php");
if ($config['admin_password'] == "secret") deb("You are using the default admin password",2); // level 2 (warning) message
mysql_connect($config['db_host'], $config['db_user'], $config['db_password']) or deb("Couldn't connect to MySQL server",1); // level 1 (fatal) message
?>
You will have noticed that this would call deb() with a level 1 (fatal) message when our script fails to connect to a MySQL server. As well as appending the message to the debug log,
calling deb() with a Level argument of 1 results in an extra one of two things, depending on the verbosity level set with set_verbosity():
- verbosity >= 1: any previously generated output is discarded, an error page is shown and script execution is stopped
- verbosity < 1: any previously generated output is discarded, a response code 500 and blank page are sent and script execution is stopped
Either way, you'll see, calling deb() with a Level argument of 1 will stop the script. This makes deb(s , 1) a great alternative to using die()
or trigger_error() to handle your feedback when things are looking fubar.
deb()'s Level parameter indicates the importance of the message. It affects the colour scheme in which the message appears in the log, as well as whether it should be shown
depending on the verbosity level set with set_verbosity(). The default value for Level is 4 (info).
dpDebug message levels
1: Fatal (halts execution)
2: Warning
3: Notice
4: Info
5: Trivial
6: Highlight
>6: Custom levels (no colour scheme)
The Label parameter, if set, overrides the message's label which appears to the left of the message in the queue. This can be handy to categorise the messages more specifically.
The boolean Code parameter specifies whether the debug message should be surrounded by <code> tags. Default is false
More information and other uses for deb() can be found in the details page.
more on set_verbosity()
Because each debug message has an associated debug level, you can specify an importance threshold to determine which messages to display. Sometimes you won't want loads of trivial messages
shown in the debug panel, but will want to see any warnings. Setting the verbosity level with set_verbosity(int Level) makes sure only messages with that level or lower (that is,
more important) are displayed. set_verbosity() affects debug messages retrospectively. verbosity.php on the demo page tests and demonstrates this.
tracing calls
dpDebug 2 can display the chain of function calls associated with each debug message. While this is possibly the most useful feature of dpDebug, callstack display must be enabled
explicitly for security reasons: to make sure nobody uses it on a live site without understanding the security basics. To enable callstack display, simply call deb_callstack(true).
Likewise, to disable it just call deb_callstack(false). When the callstack is enabled, moving your mouse over the filename and line appended to each debug message will display
a tooltip with the chain of calls that resulted in deb() being called. A list of calls will also be shown on the dpDebug error page if a call to deb() is made with
a Level argument of 1.
