Supporting nested shortcodes in WordPress

If you’re writing your own [shortcodes] or have done in the past then its important that you follow this little tip. It will only take a couple of minutes to implement and it will save your end-users from problems.

The way that WordPress extracts the shortcode tags from a post to process them is done using a regular expression. There is some debate as to whether or not this is the best approach but that’s a topic for the

I actually love regular expressions (as you can tell from the extensive library of regular expressions I’ve submitted on but in general they are feared by developers as they use an arcane syntax which is confusing to look at and hard to debug. Anyway, I digress, the point is that they have one big limitation – they don’t support nesting.

So if your shortcodes are containers and they wrap some other content like [example]content[/example] then you will need to make sure you do the nesting for WordPress and process that content for other shortcodes.

This is done simply through calling do_shortcode() on the shortcodes contents variable.

A simple example

If your simple shortcode for wrapping a block in a section tag looked like this initially:

function shortcode_section($atts, $content = null)
    return '<section>' . $content . '    </section>';
add_shortcode('section', 'shortcode_section');

To support nesting you just need to change it to this:

function shortcode_section($atts, $content = null)
    // I'm your new friend!
    $content = do_shortcode($content);

    return '<section>' . $content . '    </section>';
add_shortcode('section', 'shortcode_section');

It’s (almost) that simple!

The Notice: Undefined index: error

One thing that I did notice when started parsing the content for other shortcodes was that in certain situations I’d see an error like this:

Notice: Undefined index: columns in /srv/www/wordpress-default/wp-content/themes/example/lib/shortcodes.php on line 47

There are two common ways you will find in shortcode tutorials, using extract() or accessing via the associative array index.

// Extract method
$title = "example";

// Array index method
$attr['title] = "example";

The extract approach is frowned upon because it creates a new $variable for every attribute in your array. There is a ticket which has methodically removed almost every instance of this from the WordPress core.

Despite these warnings it does seem to solve the problem. I hate doing obviously bad code but in this instance I don’t know why its happening and I can’t figure out a better solution. If you come up with one please do post in the comments and I’ll update this article.

1 comment :

GJ said...