Overview
Debugging in a scripting language can be problematic. There are several PHP debugging options that are growing in both functionality and popularity. However, these options generally require server access and special configuration. Most programmers revert to simply printing variables to the browser via clumsy print() or print_r() statements. Neither of these solutions is ideal. Both can interfere with page layout and the results can be interspersed throughout the page and be difficult to find. With this tutorial, you will be able to create a library that will alleviate these issues.
Learning Objectives
You will learn to use PHP to write JavaScript. This will allow you to write messages to a remote debugging window without interrupting your site layout. You will learn to apply basic string substitution, and briefly examine a method of capturing data on PHP?s STDOUT via output buffering.
Background Information
JavaScript will allow you to open a blank child browser window. This is a window that has no URL location. You may then use JavaScript to write text to this window. Internet browsers will track child windows across the browser session. This will allow you to write text to this window as you move from page to page on your site. You will be able to open a remote window, and write debug information to that window on each page load of your site, tracking variables as they are manipulated.
Prerequisites
This script requires Output Control. This has been present in PHP since 4.0 and does not require an extension. In addition, your client browser must have JavaScript enabled.
How it Works
Opening a Debug window
This library uses a private function to open a remote window. You should never need to call this function directly. The function uses a static variable to track whether or not a remote window has been opened. Static variables will persist across multiple requests to a function. This will allow the script to determine if a window is currently open. If the window opening JavaScript has not been printed, this function will do so.
function debug_open_window()
{
static $window_opened = FALSE;
if(!$window_opened)
{
?>
<script language="JavaScript">
debugWindow = window.open("","debugWin","toolbar=no,scrollbars,width=600,height=400");
debugWindow.document.writeln('<html>');
debugWindow.document.writeln('<head>');
debugWindow.document.writeln('<title>PHP Remote Debug Window</title>');
debugWindow.document.writeln('</head>');
debugWindow.document.writeln('<body><font face="verdana,arial">');
debugWindow.document.writeln('<hr size=1 width="100%">');
</script>
<?
$window_opened = TRUE;
}
}
Printing a Debug Message
A call to debug_msg() will print a message to the remote window. This function first calls the debug_open_window() function to assure that the remote debug window is present, and then prints JavaScript code that will write text to the remote window.
<?php function debug_msg($mesg)
{
debug_open_window();
print "<script language='JavaScript'>\n";
print "debugWindow.document.writeln('".trim(nl2br($mesg))."<br>');\n";
print "self.focus();\n";
print "</script>\n";
}
By using the writeln() method of the remote window document, we can add new text to the debug window. The debug_msg() function converts any newline characters to html linebreaks to assure that the script output is uninterrupted.
Printing a Debug Variable
Printing a debug variable with the debug_var() function is very similar to the debug_msg() function. The debug_msg() function takes two parameters; $name, a string name of the variable or a message to print, and $data, an unknown variable, which is typically an array. PHP has supplied the print_r() function for printing array data in a human readable way. However, print_r() prints directly to STDOUT. In order to direct this to the debug window, the script must capture the output, and then write it to the remote window via JavaScript. The script uses the PHP output buffering functions to capture the output of the print_r() function. The private function debug_capture_print_r() will return the output of print_r() as a string.
<?php function debug_capture_print_r($data)
{
ob_start();
print_r($data);
$result = ob_get_contents();
ob_end_clean();
return $result;
}
To make the debug window most effective, this script uses an additional step to colorize the output. This is done with simple substitution with the PHP str_replace() function. HTML font tags are inserted around certain characters to produces the desired colors.
<?php function debug_colorize_string($string)
{
/* turn array indexes to red */
$string = str_replace('[','[<font color="red">',$string);
$string = str_replace(']','</font>]',$string);
/* turn the word Array blue */
$string = str_replace('Array','<font color="blue">Array</font>',$string);
/* turn arrows graygreen */
$string = str_replace('=>','<font color="#556F55">=></font>',$string);
return $string;
}
The output from the captured and color coded print_r() function spans multiple lines. The JavaScript writeln() method cannot accept strings with newline characters. The captured results of the print_r() statement will be processed with the explode() statement to separate the output lines. The debug_var() function will use a JavaScript writeln() method call to write each of these output lines. Additionally, the HTML <pre> tag will be used to preserve spacing and layout.
<?php function debug_var($name,$data)
{
debug_open_window();
$captured = explode("\n",debug_capture_print_r($data));
print "<script language='JavaScript'>\n";
print "debugWindow.document.writeln('<b>$Name</b>');\n";
print "debugWindow.document.writeln('<pre>');\n";
foreach($captured as $line)
{
print "debugWindow.document.writeln('".debug_colorize_string($line)."');\n";
}
print "debugWindow.document.writeln('</pre>');\n";
print "self.focus();\n";
print "</script>\n";
}
The Script
This script uses the PHPDoc commenting system from
http://phpdoc.de/. It is intended as an included library file.
<?php
/**
* print debug information to the current debug window
*
* @access public
* @param $name string variable name
* @param $data unknown variable
* @return null
* @global
*/
function debug_var($name,$data)
{
debug_open_window();
$captured = explode("\n",debug_capture_print_r($data));
print "<script language='JavaScript'>\n";
print "debugWindow.document.writeln('<b>$sName</b>');\n";
print "debugWindow.document.writeln('<pre>');\n";
foreach($captured as $line)
{
print "debugWindow.document.writeln('".debug_colorize_string($line)."');\n";
}
print "debugWindow.document.writeln('</pre>');\n";
print "self.focus();\n";
print "</script>\n";
}
/**
* print a message to the debug window
*
* @access public
* @param $mesg string message to display
* @return null
* @global
*/
function debug_msg($mesg)
{
debug_open_window();
print "<script language='JavaScript'>\n";
print "debugWindow.document.writeln('".trim(nl2br($mesg))."<br>');\n";
print "self.focus();\n";
print "</script>\n";
}
/**
* open a debug window for display
*
* this function may be called multiple times
* it will only print the code to open the
* remote window the first time that it is called.
*
* @access private
* @return null
* @global
*/
function debug_open_window()
{
static $window_opened = FALSE;
if(!$window_opened)
{
?>
<script language="JavaScript">
debugWindow = window.open("","debugWin","toolbar=no,scrollbars,width=600,height=400");
debugWindow.document.writeln('<html>');
debugWindow.document.writeln('<head>');
debugWindow.document.writeln('<title>PHP Remote Debug Window</title>');
debugWindow.document.writeln('</head>');
debugWindow.document.writeln('<body><font face="verdana,arial">');
debugWindow.document.writeln('<hr size=1 width="100%">');
</script>
<?
$window_opened = TRUE;
}
}
/**
* catch the contents of a print_r into a string
*
* @access private
* @param $data unknown variable
* @return string print_r results
* @global
*/
function debug_capture_print_r($data)
{
ob_start();
print_r($data);
$result = ob_get_contents();
ob_end_clean();
return $result;
}
/**
* colorize a string for pretty display
*
* @access private
* @param $string string info to colorize
* @return string HTML colorized
* @global
*/
function debug_colorize_string($string)
{
/* turn array indexes to red */
$string = str_replace('[','[<font color="red">',$string);
$string = str_replace(']','</font>]',$string);
/* turn the word Array blue */
$string = str_replace('Array','<font color="blue">Array</font>',$string);
/* turn arrows graygreen */
$string = str_replace('=>','<font color="#556F55">=></font>',$string);
return $string;
}
?>
Example Use
To use these function, include() this library into your script. Then you may call both debug_msg() and debug_var() anywhere within your script.
A simple way to examine incoming URL parameters would be to examine the superglobal array $_GET.
debug_var('Get Parameters', $_GET);
You might want to examine how a variable is changing inside a loop.
for($i = 0; $i < 10; $i++){
$j = $i * 10;
debug_msg("$i => $j");
}
You could use the debug_msg() command to determine progress through a long running script. This example will use flush() to push the output to the browser and attempt to present this in real time.
$start_time = time();
debug_msg('Starting query');
flush();
/* long running query */
debug_msg('Finished query in '. (time() - $start_time). ' seconds');
flush();
Tips
This script could be extended in several ways. A global variable could be used to turn on and off all output from the debug functions. This would allow you to leave the debug statements in the production code, and simply turn them on when needed. Additionally, you could test for the remote IP of the browser, and only return the debug window for developer IP addresses.
http://www.zend.com/zend/tut/tutorial-DebugLib.php