New in symfony 1.2: Customize the Web Debug Toolbar

The symfony web debug toolbar is one of the developer best friend. It is always conveniently accessible in the browser when using the development environment. It gives you everything you need to know about the current page and ease the debugging of your applications. Until now, all the information available in this toolbar were hardcoded. But as of symfony 1.2, the web debug toolbar is entirely configurable.

The web debug toolbar

Without any configuration, the default toolbar looks like this:

The default web debug toolbar

The toolbar is composed of panels. Each panel is composed of a title and an optional panel content, and the panel is represented by a PHP object. By default, there are seven panels:

Name Class name Representation
symfony version sfWebDebugPanelSymfonyVersion symfony version
cache information sfWebDebugPanelCache cache
configuration sfWebDebugPanelConfig configuration
logs sfWebDebugPanelLogs logs
database information sfWebDebugPanelDatabase database
memory usage sfWebDebugPanelMemory memory
timer sfWebDebugPanelTimer timer

Customizing the toolbar

You can customize the web debug toolbar by listening to the debug.web.load_panels event in your application or project configuration file. The following code shows you how to listen to this event in the frontend configuration class:

<?php
 
class frontendConfiguration extends sfApplicationConfiguration
{
  public function configure()
  {
    // ...
 
    $this->dispatcher->connect('debug.web.load_panels', array($this, 'configureWebDebugToolbar'));
  }
}
 

With this configuration in place, symfony will automatically calls the configureWebDebugToolbar() method when it initializes the web debug toolbar. This method can then remove, replace, or add panels.

As for every listener, symfony calls the configureWebDebugToolbar() method with an event as an argument. The subject of the event is the web debug toolbar object we want to manipulate:

<?php
 
class frontendConfiguration extends sfApplicationConfiguration
{
  public function configure()
  {
    $this->dispatcher->connect('debug.web.load_panels', array($this, 'configureWebDebugToolbar'));
  }
 
  public function configureWebDebugToolbar(sfEvent $event)
  {
    $webDebugToolbar = $event->getSubject();
  }
}
 

Removing a panel

To remove a panel, call the removePanel() method with the name of the panel like this:

<?php
 
class frontendConfiguration extends sfApplicationConfiguration
{
  // ...
 
  public function configureWebDebugToolbar(sfEvent $event)
  {
    $webDebugToolbar = $event->getSubject();
 
    $webDebugToolbar->removePanel('memory');
  }
}
 

Replacing or adding a panel

To replace a panel, call the setPanel() method with an existing name.

To create a panel, call the same setPanel() method but give it a unique new name.

You can of course extends an existing panel class to add or remove some information but for the sake of the example, let's create a brand new panel that displays the JavaScript and stylesheet files that are registered within our response object:

<?php
 
class frontendConfiguration extends sfApplicationConfiguration
{
  // ...
 
  public function configureWebDebugToolbar(sfEvent $event)
  {
    $webDebugToolbar = $event->getSubject();
 
    $webDebugToolbar->setPanel('assets', new sfWebDebugPanelAssets($webDebugToolbar));
  }
}
 

We need to create the sfWebDebugPanelAssets class. All panel classes must extend sfWebDebugPanel and implement at least the following three abstract methods:

  • getTitle(): Returns the HTML displayed on the web debug toolbar. If it returns null, the panel won't be displayed.
  • getPanelContent(): Returns the HTML representation of the panel. If it returns null, the title won't be clickable.
  • getPanelTitle(): Returns the title of the panel when it is displayed.

With this information in mind, here is the simplest panel that can possibly work:

<?php
 
class sfWebDebugPanelAssets extends sfWebDebugPanel
{
  public function getTitle()
  {
    return 'assets';
  }
 
  public function getPanelTitle()
  {
    return 'Stylesheet and JavaScript files from sfWebResponse';
  }
 
  public function getPanelContent()
  {
    return null;
  }
}
 

It will display a new 'assets' entry in the web debug toolbar but the text is not clickable because the panel content is empty.

Let's make it a bit more interesting by adding some content to the panel. The response object has two methods, getJavascripts() and getStylesheets(), and these methods return an array of JavaScript and stylesheets files included by the response object.

The getPanelContent() method iterates through these two arrays to construct the panel content and returns it as an HTML string:

<?php
 
class sfWebDebugPanelAssets extends sfWebDebugPanel
{
  // ...
 
  public function getPanelContent()
  {
    $response = sfContext::getInstance()->getResponse();
    $html = '';
 
    if ($stylesheets = $response->getStylesheets())
    {
      $html .= '<h2>Stylesheets</h2><ul>';
      foreach ($stylesheets as $file => $options)
      {
        $html .= sprintf('<li><strong>%s</strong> %s</li>', stylesheet_path($file), count($options) ? '('.var_export($options, true).')' : '');;
      }
      $html .= '</ul>';
    }
 
    if ($javascripts = $response->getJavascripts())
    {
      $html .= '<h2>Javascripts</h2><ul>';
      foreach ($javascripts as $file => $options)
      {
        $html .= sprintf('<li><strong>%s</strong> %s</li>', javascript_path($file), count($options) ? '('.var_export($options, true).')' : '');;
      }
      $html .= '</ul>';
    }
 
    return $html;
  }
}
 

Here is the result on a symfony page:

The customized web debug toolbar

Package your panels as a plugin

You can even package your new panels and distribute them as a symfony plugin.

When a user install your plugin, you can ask him to change his configuration class to add the event listener as we have done before, but you can also connect to the web.debug.load_panels event in your plugin config.php file like this:

<?php
 
// ...
 
require_once '/path/to/sfWebDebugPanelAssets';
 
$this->dispatcher->connect('debug.web.load_panels', array('sfWebDebugPanelAssets', 'listenToAddPanelEvent'));
 

Then, add the listenToAddPanelEvent() method to your sfWebDebugPanelAssets class:

class sfWebDebugPanelAssets extends sfWebDebugPanel
{
  static public function listenToAddPanelEvent(sfEvent $event)
  {
    $event->getSubject()->setPanel('assets', new sfWebDebugPanelAssets($event->getSubject()));
  }
}
 

That way, your panel will to be automatically added to the web debug toolbar without any change from the developer.

That's all there is to it. Thanks to the new web.debug.load_panels event, you are free to customize the web debug toolbar the way you want. If you want more information or learn more tricks, you can browse the code for the built-in panels.

And as always, this tutorial is permanently available from the cookbook.

Comments

I think two classes are mixed up. Shouldn't it be:

database information : sfWebDebugPanelPropel
timer : sfWebDebugPanelTimer
I know we're talking about 1.2, but is this patchable for 1.1? :D
Definitely a nice feature =)
Nice!
But, what about MVC implementation? Too much html for a class...
@Ruf: I think it is pretty easy for you to return a `get_partial()` or a `get_component()` in the `getPanelContent()` method. That way, you have free MVC separation!
Genius
@Ruf: We are talking about the web debug toolbar, a tool to help you debug your code. I don't think we need to be MVC here. And as François said, you can create symfony partials or components if you want.
@Francois Zaninotto and @fabien : sorry, Id didn't want to hurt anybody.

I was just thinking about extending an existing panel. In that case, the MVC implementation would be really useful...

But ok, you are right, it is simply a debug toolbar...
Cool!!!
We look forward to symfony 1.2.
Are you currently planning a FirePHP extension port of this incredibly useful debugging system? This would be really awesome for us, Firtefox users!
Nice, i like it. :)

Comments are closed.

To ensure that comments stay relevant, they are closed for old posts.