Debugging PHP Code with FirePHP

Tweet

As the technical manager of a suite of software projects, one of my duties is doing code reviews. One of the things I see far more often than I’d like when doing reviews is debugging PHP code committed and pushed up the chain. Raise your hand if you’ve done this:

<?php
$total = $someService->getComplexTotal();
var_dump($total);
$someOtherService->processTotal($total);

Ok, so you don’t have to raise your hand. But chances are, you smiled a bit. Either “Yeah, how did he know I do that?” or “Yeah, I used to do that. But I have other ways of debugging now.” What an awesome segue!

We should use a debugging method that does not display information to a non-programmers through the web page. That is to say, we should never see variable dumps or SQL statements echo’d to the screen. This can’t happen.

The safest method of debugging requires configuring your IDE to use a tool like Xdebug or Zend Debugger to trace currently executing code. This isn’t always practical. In the absence of using a fully configured debug environment, I turn to FirePHP.

Use FirePHP for Safer PHP Debugging

FirePHP is a PHP library used to generate console messages in the browser from your PHP code. Console messages are generally created by JavaScript, but FirePHP sends similar messages as HTTP headers with the response from the server. Various browser extensions act as proxies for the JavaScript console methods and convert these headers into console log messages.

Why is this “safer” that outputting debug data? Well, when using methods like JavaScript’s console.log(), you need to make a concerted effort to view the debug messages. Normal visitors would not have the development console open on their browser, and therefore will not see your debug messages. If you’re outputting var_dump()‘s instead, there is no way NOT to see the messages.

Do note, I call this “safer,” but you still must take care not to leave FirePHP calls enabled with critical system information available in production.

Setting Up FirePHP

Visit the FirePHP Core Install page to choose to download the library directly or get the commands to install via PEAR. If you use other frameworks, such as Zend Framework, log writers already exist for FirePHP so an additional download is not necessary.

If you’re using Firefox, install FireBug for an expanded set of tools to debug your web applications, and then install the FirePHP extension to extend FireBug and intercept the FirePHP messages.

If you are using Chrome, simply install one extension FirePHP4Chrome. (Full disclosure: I created this Chrome extension and actively develop it. I hadn’t had a lot of luck with other plugins in Chrome.)

Finally, open the console in your browser of choice. In Firefox, click the Firebug icon to display the console. Verify FirePHP is enabled by clicking the FirePHP icon in the console view and noting whether the check mark is next to enabled. In Chrome, click the settings icon, choose tools, and click ‘Developer Tools’ to display the console. FirePHP4Chrome will be automatically enabled when the console is opened.

Basic Logging With FirePHP

If you’ve ever worked with different error level logging, you’ve undoubtedly seen the debate on the differences between what types of errors should be “warn” versus “error.” Or, whether you should you use “info” versus “log.” While I have my own opinions, I won’t bore you with them. Suffice to say there may be reasons to differentiate a message you want to send to the console in a different way than a standard message.

The four types of messages that the FirePHP protocol supports currently are: log, info, warn, and error. I aim to show you how you can use each one. It is up to you to choose the proper one for your environment and the context of your debugging/logging.

The following code demonstrates how to include the FirePHP library and execute each of the logging types.

<?php
require 'FirePHPCore/fb.php';

FB::log('Log message');
FB::info('Info message');
FB::warn('Warn message');
FB::error('Error message');

The above code simply includes the core FirePHP library and calls the various logging methods. There are multiple ways to call the library methods; it supports pure global function procedural calls, static method calls, and fully instantiated object calls. I use the static methods because I generally want to use FirePHP in only one line to send debug information; I want to do it quickly!

Below, you’ll see screenshots with the output on both Firefox and Chrome.

Firefox’s display is a bit prettier. The Chrome version does not have an icon for “info” because console.log() in Chrome does not support the icon yet. You’ll also notice later that the table output in Chrome is very rudimentary. This is because Chrome does not support console.table() in the non-experimental feature-set yet. While I use Chrome for my own development, I’ll use Firefox in my screenshots in the rest of this article.

To make the logging information more useful, it is possible to add a label to the log messages. This is specified as the second parameter of the logging methods.

<?php
require 'FirePHPCore/fb.php';

$turtles = $zooService->fetchAllTurtles();
FB::info($turtles, "All Turtles");

As you see in the following screenshot, the zoo service in my example fetched three turtles. It was clear that this particular log message was turtles, and say, not famous actors or something else. To save space, Firebug/FirePHP has collapsed the output. Hover over the line to see the entire output.

Advanced Logging With FirePHP

FirePHP is great for generating one-time log messages as I’ve already demonstrated. However, there are more advanced features which make FirePHP really shine. Three that I’ll cover quickly are message grouping, tables, and traces.

Grouping

Let’s pretend we have a bit of logging that we need to do in a loop. It could potentially get out of hand and make the console scroll. We may want to put that it a group so that it can easily be collapsed and expanded. In this sample code, I’ll log four entries of information I want grouped together.

<?php
require 'FirePHPCore/fb.php';

$specialHashes = array();
for ($x = 0; $x < 5; $x++) {
    $specialHashes[$x] = sha1($x . 'somesalt');
    FB::info($specialHashes[$x], "Hash #" . $x);
}

And the output:

The log message is slightly longer than I might like. I want to group it intelligently and allow for it to be collapsed. I have modified the code to include grouping methods built into FirePHP.

<?php
require 'FirePHPCore/fb.php';

$specialHashes = array();
FB::group('Special Hashes');
for ($x = 0; $x < 5; $x++) {
    $specialHashes[$x] = sha1($x . 'somesalt');
    FB::info($specialHashes[$x], "Hash #" . $x);
}
FB::groupEnd();

And now, the new output:

As you can see, there is now an option to minimize the group labeled “Special Hashes.” FirePHP even allows for a group to be collapsed by default. Simply replace the group() call and add an options array as follows:

FB::group('Special Hashes', array('Collapsed'=>true));

Tables

FirePHP can send tabular data pretty easily. In fact, since Firefox’s console.table() method is so great, the data displays beautifully and is easy to understand. (Oh Chrome, please catch up!) To send table data in FirePHP, an array of columns is used. However, the first element of the array must be the column names. Using our special hashes example, let’s log them as a table:

<?php
require 'FirePHPCore/fb.php';

$specialHashes = array();
for ($x = 0; $x < 5; $x++) {
    $specialHashes[] = array($x, sha1($x . 'somesalt'));
}

$headers = array('Hash #', 'Hash Value');
$logTable = array($headers) + $specialHashes;
FB::table("Special Hashes", $logTable);

You may notice one thing that I did a bit differently is the creation of the $logTable variable. Since the first element of the array sent to the table() method needs to be column labels, I created a temporary variable to do this task for me. I don’t want to manipulate the $specialHashes array – that is my business data. The table() method takes a Label parameter followed by the array of columns.

When the console loads the data from FirePHP, the table is collapsed. For the above screenshot, I clicked the label to display the data.

Traces

Traces can be invaluable when debugging PHP code. Whether you want to couple them with a log message showing a variable’s value at specific point in the code or you need more information from an exception, trace is where it’s at. Very simply, you can call a trace anywhere in your PHP code with FirePHP as shown here:

<?php
require 'FirePHPCore/fb.php';

FB::trace('Simple Trace');

And the output:

When the output was displayed, I simply clicked on the label of “Simple Trace” to display the entire trace. This example is very simple and shows how easily it can be inserted into any code.

Let’s make a bit more complex of an example (although, I doubt you’ll actually want to make code like this in your project):

<?php
require 'FirePHPCore/fb.php';

function one() {
    echo "This is one.<br>";
    two();
}

function two() {
    echo "This is two!<br>";
    FB::trace('Trace from function two.');
}

one();

Here, function one() is being called. Function two() is invoked in the last line of the function. After two() finishes, it executes a trace call to FirePHP. The following screenshot shows Firefox’s output:

… And There’s More!

There is much more you can do with FirePHP than just the above examples. To view more information about specifying other options, setting up exception traces, and even using assertions, check out the FirePHP Headquarters.

A Bonus Example

As with most introduction style articles, the information here has been relatively generic and the examples mild. Need a bit more in-depth example to persuade you to use FirePHP? Let’s go:

<?php
try {
    $key = "user.{$userId}";
    if (!SimpleCache::has($key)) {
        FB::info($key, 'Cache miss:');
        $userService = new UserService();
        $user = $userService->fetchById($userId);
        SimpleCache::set($key, $user);
    }
    $user = SimpleCache::get($key);
}
catch (Exception $e) {
    FB::error($e);
    Router::generate500();
}

This simple example code is built to retrieve a user object. This system uses some type of caching and throws exceptions when a user is not available. The first time this code runs, the user is not cached so FirePHP sends an info message. For user ID of 5, the following screenshot shows the output.

The second time the code is ran, the method info() is not called and no message is sent to the console. Now, let’s imagine that the cache has become unavailable. (I could have created the code so that it could run if the cache wasn’t present, but that would just take away all of the fun!) In the case where the cache is no longer available, the set() method will throw an exception. The screen will show a nice error 500 message, according to this code. But, I’d like to see the exception and stack trace right away. That’s where the error() method comes in. It can interpret the Exception object and log it nicely for me.

Summary

The safest way to debug your PHP code is to use a debugger environment set up within your IDE. However, for quicker debugging as well as application level logging, FirePHP can play an important role.

Image via Fotolia

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • lingtalfi

    Alternatively, if you’re in a hurry, you can add this function to your library :
    d($v){
    if(‘88.99.35.42′ == $_SERVER['REMOTE_ADDR']){
    var_dump($v);
    }
    }

    Then go here to get your ip :
    http://www.monip.org/
    and update the function with your real ip.

  • Alexander Cogneau

    How does this affect the page rendering speed?

    • Aaron Saray

      There is minimal impact to the page delivery. Remember, all FirePHP is doing is sending additional headers. So, there should be no impact to the ‘rendering,’ but minimal to the actual delivery of the content. In cases where even the smallest amount of header traffic is too much, FirePHP should be disabled. This is probably also in your production environment, so these two goals align :) (You should not have FirePHP logging in production.)

    • http://www.ok.cl Juan Manuel Doren

      probably you will use something like this
      $debugging = true; // or $ip = ‘my ip….’
      if ( $debugging )
      {
      FB::……

      • Basil

        @Juan FB has a setEnabled(). It is probably preferable to set this globally by default in a real app.

        require ‘FirePHPCore/fb.php';
        $debugging = true; // or $ip = ‘my ip….’
        if ( $debugging ) {
        FB::setEnabled(false);
        }

        FB::…();
        FB::…();

        etc.

  • http://neilgirardi.com Neil

    Another option is to use the error_log() function. It will print whatever string or expression you pass it in the Apache error log. You can tail the log passing in the -f parameter to monitor the log output in real-time.

    For example, let’s say you’re troubleshooting an HTML form page and you’re not sure if the correct values are being submitted. Open a new terminal window and enter the following:
    $ tail -f /var/log/apache2/error.log

    (Note, this example assumes you’re using Linux. The location of the Apache log is different if you’re using Macports on OS X.)
    This will display the last entry entry of the error log.
    Now add the following to your form handler:
    error_log(print_r($_POST, true));
    As soon as you submit the form, the post data will appear in the terminal.

  • http://ehsana.info metasanana

    This is a decent article. Although I FirePHP seems like a bit much for the average PHP driven website design process. I suppose it could be used for higher tier unit tests..

  • Aveesh Kumar

    Thx for a wonderful tutorial. I, like the other posters above, use the above techniques but FirePHP is much more elegant.

    I have adapted your tutorial for my usage by logging only if I am in debug mode (global included boolean constant) and that too only if the $_SERVER['REMOTE_ADDR'] is my current IP (also a global variable)

  • http://blog.mindplay.dk Rasmus Schultz

    There’s a (much) better solution for Chrome:

    https://github.com/ccampbell/chromephp

    By design, this is just so much more elegant – it basically serializes console.*() calls on the server-side and sends them as headers to the client. This is so much less complicated, and causes a lot less problems than FirePHP.

    Chrome’s developer tools are well up to the standards of FireFox and FireBug at this point – for me, ChromePHP was the last missing piece…

  • http://alexfraundorf.com Alex Fraundorf

    Aaron,
    Thank you for the article. I think it does a great job of showing how to use the core features of FirePHP. A lot better than var_dump()! Thank you for including the more in depth example along with the basic ones.

  • Anton Visser

    Aaron, great extension, thanks for working on this. Do you know why FirePHP4Chrome won’t work in Chromium?

    • Anton Visser

      Turns out, I had Chromium 20 installed and the FirePHP4Chrome extension has 21 as the minimum.

  • http://memeLab.com.au/ Tim Osborn

    Can you tell me: what are the meanings of the coloured dots that appear beside some arrays, objects, etc in the popup? I can’t find a reference (got a stackexchange question open, would like more detail: http://stackoverflow.com/questions/13577773/what-do-the-coloured-dots-in-firephp-output-mean )
    Thanks!

  • Martin Vrkljan

    Keep in mind that some servers limit the size of outputted HTTP headers. I’m using FirePHP to log a lot of information in my framework, and I found out that I have to disable it on nginx servers. If you’re planing on using FirePHP to log/test your production environment, be sure to check if there are limits to HTTP headers size.