JavaScript Powered PHP Debugging

Intended Audience
Overview
Learning Objectives
Background Information
Prerequisites
How it Works
•  Opening a Debug window

•  Printing a Debug Message
•  Printing a Debug Variable
The Script
Example Use
Tips
About the Author


Intended Audience


This tutorial is intended for PHP programmers who want a more transparent way to debug PHP applications. Experience with JavaScript window functions is assumed.

If you want to read more about the JavaScript techniques used in this tutorial, I suggest the following pages at CNet’s Builder.com:


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.


About the Author


Craig Davis is a Senior Programmer for

Learningstation.com. He has spent
the last four years developing large user management and content distribution
systems. Craig lives in the Pacific Northwest and is rarely seen without a
mountain bike or snowboard close at hand.

Published: June 18th, 2003 at 12:00
Categories: Uncategorized
Tags: , ,

6 comments to “JavaScript Powered PHP Debugging”

There is a small typo on line 16 of the full scripted example. It should be $name instead of $sName. Otherwise great example. I have been meaning to do something like this for ages, guess you beat me :)

Hi,
I have been wondering how to get a debug environment for php since I been
program php. I would like to use your example, but I am not so sure how…

1. for "function debug_open_window() "
should I put this in a php file in server side?

2.should I put the rest of the functions in the same file?

function debug_msg($mesg)

function debug_capture_print_r($data)

function debug_colorize_string($string)
function debug_var($name,$data)

3. and then put the script function in another php file in server side?

4. how to use output control?

5. how to use these function?

Could you give a step by step example?

Many thanks,

Ruichen

_____anonymous_____
July 25th, 2007 at 9:09 pm

Under the debugging variables section
"Printing a debug variable with the debug_var() function is very similar to the debug_msg() function. The debug_msg() function takes two parameters;"

Should be:
"Printing a debug variable with the debug_var() function is very similar to the debug_msg() function. The debug_var() function takes two parameters;"

I’m going to give all this a try. thanks!

_____anonymous_____
August 15th, 2007 at 11:07 pm

Add this in debug_msg and debug_var before self.focus()

print "debugWindow.scrollBy(0,10000);";

This will keep the debug window scrolled to the bottom, until
you hit ten thousand pixels of backlog.

The above solution writes the debug code into the page content. This can be a problem when you need to debug AJAX calls where the response must be valid JSON or XML code.

The FirePHP tool gets around this limitation by sending the debug data in the response headers instead of the page content.
The debug data is then displayed in the Firebug Console.

You can find more information at: http://www.firephp.org/

_____anonymous_____
October 3rd, 2008 at 3:30 am

Hi, thanks for your article.
You can get around the need for buffering print_r out put by passing it its second optional argument of a boolean. This causes it to return its would be output to a variable.

$myPrint_Routput=print_r($someObject, true);

cheers