"Debugging software is not exactly a fun job for developers. The most widely used debugger for PHP still seems to be a var_dump() statement, possibly in conjunction with die() to halt program execution at a certain point.
"While there is nothing wrong using var_dump() statements in itself, you still need to change the program code to debug a PHP script. And worse, after you have finished debugging, you must remove all var_dump() statements again (well you should, at least). It may well be that a few days later you'll find yourself adding the very same var_dump() statements to your code again because you need to go hunting another bug.
"Of course, you could just comment out the var_dump() statements, but that looks really ugly in the code. Another option would be to wrap the var_dump() in conditional clauses, and only execute them when, say, a constant DEBUG is defined. This affects performance, because even if the var_dump() statements are not executed, the conditional clause must be executed. And besides, it looks even uglier in the code."
- Zend Developer Zone
The archive of this code and text: debug.zip.
This presents some debug code as a way to have all the benefits of var_dump() statements as Zend Developer Zone says, but without any of the drawbacks they outline. This debug code creates no global data with one exception explained below. It is an API and not a Class.
Instead of var_dump()
you use debug()
which works similarly, but with a few major exceptions. The displayed data:
In addition, the displayed data includes the file and function name in which the debug()
call is used. The result is a list of diagnostic data displayed unobtrusively and under control providing a complete runtime view of the code being debugged.
The basic operation is to include the debug.php file, and strategically place debug calls throughout your code. If you do just that, nothing will happen and any performance hit is very small.
To enable the debug()
function's output, you call the companion function debuglog()
with a value to indicate how the debug output will be displayed:
$GLOBALS['debug_msgs']
variable, a string, with each message appended with a <BR>. This is so the data can be incorporated into an application's own HTML output.Here is an example for the latter value:
<div id="debug"> <?php print $GLOBALS['debug_msgs']; ?> </div>
With some appropriate CSS the text can displayed however one sees fit.
NOTE: The code should call debuglog()
to enable debug output only if logged in as an Administrator so that no other visitor sees the output.
The debug()
function has two optional arguments. With no arguments the current debug log status is returned; initially 0 or the value set by debuglog()
.
The first argument is the variable to be displayed. This is usually a string or an array, but booleans, resources and objects are handled in a meaningful way (a full list is below). Arrays are imploded into a string with associative arrays handled correctly.
The second argument is overloaded. If -1 the message is displayed immediately. If a positive number the message is truncated to it (to avoid possibly overly long strings). If 'type
' the first argument is converted by gettype()
.
Additional information is displayed for each message -- the file, function and line number of the debug()
statement, and optionally, the file and line number of the function that called the function that has the debug()
statement.
Here is an example of several debug()
messages at the end of a program:
message log: (index.php,37)(error.php,51,modules_init) _config_display (display.php,742)(data.php,12,load_php_file) 'translate.ini' (html.php,47)(data.php,12,load_php_file) 'htm/templates.php' (mysql.php,130)(mysql.php,349,_mysql_query) SELECT id FROM `root` ORDER BY id (mysql.php,130)(mysql.php,353,_mysql_query) Object: mysqli_result (mysql.php,130)(mysql.php,368,_mysql_query) Array: {1,2,3,4,5,6}
All messages will be converted by htmlentities()
so that HTML in any message will be displayed properly.
There is a companion function to control debug options during run-time, _debug()
, and it takes two arguments: an option name and an option value. The options and their default values are:
msg "<b>message log:</b>" open "\n<div style='background:#fff;color:#000;'><pre>" close "</pre></div>" caller 1 newline 1 trunc 0 entities 1 file NULL nofile NULL
The open
and close
strings get displayed before and after the debug output, msg
precedes the debug message list. The caller
option adds the caller file and line number preceding the message. The newline
option converts newlines in the data to \n
. The trunc
option will cause all messages to be truncated to it's value. If entities
is set to zero the output will not be converted by htmlentities()
.
The file
and nofile
options can be used to limit message output. The file
option limits messages to messages only from that file. For example, with the call _debug('file','mysql.php')
, the output example would be:
message log: (mysql.php,130)(mysql.php,349,_mysql_query) SELECT id FROM `root` ORDER BY id (mysql.php,130)(mysql.php,353,_mysql_query) Object: mysqli_result (mysql.php,130)(mysql.php,368,_mysql_query) Array: {1,2,3,4,5,6}
And with the call _debug('nofile','data.php')
, the output example would be:
message log: (index.php,37)(error.php,51,modules_init) _config_display (mysql.php,130)(mysql.php,349,_mysql_query) SELECT id FROM `root` ORDER BY id (mysql.php,130)(mysql.php,353,_mysql_query) Object: mysqli_result (mysql.php,130)(mysql.php,368,_mysql_query) Array: {1,2,3,4,5,6}
If the _debug()
function is called without a value, i.e. _debug('newline')
, that option's current value is returned.
There are a few "internal" functions (all begining with an underscore) that may be of use outside of debug; hopefully they are written well enough to be self-documenting.
In addition to controling debug by setting options as described above, all options can be set by defines before including the debug file:
DEBUG_LOG DEBUG_MSG DEBUG_OPEN DEBUG_CLOSE DEBUG_EMPTY DEBUG_CALLER DEBUG_NEWLINE DEBUG_ENTITIES
DEBUG_LOG
is used to set the log level; same as debuglog(DEBUG_LOG)
.
Here is a typical, although rather simplistic, use:
<?php
// file test.php
include "debug.php";
debuglog(1);
function funca($arg) {
debug($arg);
echo "function A $arg<br>";
funcb();
}
function funcb() {
debug("");
echo "function B<br>";
}
funca("this is a test");
?>
And the output is:
function A this is a test function B
message log: (test.php,19)(test.php,9,funca) 'this is a test' (test.php,11)(test.php,15,funcb)
Passing an empty string, debug("")
results in just a "breadcrumb", useful to follow code flow.
In the above example, turning off debugging is simply commenting out the call to debuglog()
or passing it a 0 if your code were to use a configuration setting of some kind. Or the define DEBUG_LOG
could be used.
The following list is how debug()
interprets special data.
Type Output
TRUE "(true)" FALSE "(false)" object "Object: ".get_class($arg) resource "Resource: ".get_resource_type($arg) empty string "''"
If your code has redirects by header("Location: $URL")
, or direct downloads by header("Content-Type: text/plain")
, any debug output will interfere. That can be mitigated by preceding such statements with:
if (debug()) debuglog(0);
to turn the output off.
I have tested this code with WordPress and it works -- sort of. Just include the debug.php file in index.php and set the debug level and then place your debug()
calls wherever. Of course, that way means every visitor will see the debug output.
WordPress says "Use capability checks..." to check for admin rights, but they do not explain what that is, so I cannot provide an example of how to test for being logged in as Admin (it's not is_admin()
) and I don't feel like tracking it down.
You can also add an option, say debug
, and use this:
debuglog(get_option('debug'));
But WordPress is so complicated that I cannot find where in the load process that get_option()
becomes available for use; i.e. many functions you may want to debug are called before get_option()
is available.
So I give up there.
Also, WordPress has more than one entry point, so including just in index.php may cause Fatal error: Call to undefined function debug
.