Filtering the output of WordPress widgets

This post is more than 12 years old.

Probably the best thing about WordPress, from my perspective as a developer, is its hooks. It has filter and action hooks for nearly everything, which means I can easily customise a WordPress website to meet pretty much any requirements thrown at me. Well, nearly any. Except widgets.

Widgets directly output to the page, with no filtering. There’s an action hook just before a widget is called (dynamic_sidebar), but no subsequent action after it’s called, so I can’t even trap the output of the widget with output buffering in PHP.

Unless… my theme doesn’t call dynamic_sidebar() directly, it calls my own function which wraps the call to dynamic_sidebar() with PHP output buffering. That lets me filter the output from each widget zone (and only the widget zones) to make my changes. Yay!

My example replaces a custom field in a SendBlaster Double-OptIn form with a drop-down list, something I just had to do for a website. The client wanted a custom field, but wanted to control the values people gave by forcing them to pick from a list. The plugin allows custom fields, but they are all text inputs, with no option to make them select elements.

First, I needed a function in my theme’s functions.php file, that wraps the call to WordPress’ dynamic_sidebar() and filters the output from the widgets.

/**
* wrapper function for showing sidebars, so that we can customise widget output
* @param int|string $index Optional, default is 1. Name or ID of dynamic sidebar.
* @return bool True, if widget sidebar was found and called. False if not found or not called.
*/
function theme_dynamic_sidebar($index = 1) {

    // capture output from the widgets
    ob_start();
    $result = dynamic_sidebar($index);
    $out = ob_get_clean();

    // replace SendBlaster OptIn widget Location custom field with drop-down list
    $list = <<<HTML
Location <select size="1" name="$1" id="$2" >
    <option value=""></option>
    <option value="ACT">ACT</option>
    <option value="SA">SA</option>
    <option value="NT">NT</option>
    <option value="QLD">QLD</option>
    <option value="NSW">NSW</option>
    <option value="VIC">VIC</option>
    <option value="TAS">TAS</option>
    <option value="WA">WA</option>
    <option value="NZ">New Zealand</option>
    <option value="US">United States</option>
    <option value="Other">Other</option>
</select>
HTML;
    $out = preg_replace('|Location <input type="text" name="([^"]+)" id="([^"]+)"(?:[^>])*>|', $list, $out);

    // finally, output whatever we have left
    echo $out;

    return $result;
}

Next, I needed to replace calls to dynamic_sidebar() in the theme.

theme_dynamic_sidebar('left_sidebar');

And job is done. Simple really, as long as you have control of the theme and how it outputs widget zones.